From 3f61b08b6026e4de8c659d9796ea40b040dc5d92 Mon Sep 17 00:00:00 2001
From: fritz <fritz@localhost.localdomain>
Date: Tue, 26 Mar 2013 10:02:35 +0100
Subject: [PATCH] support for dvbsky-usb receiver

---
 drivers/media/dvb/dvb-usb/Kconfig            |    1 +
 drivers/media/dvb/dvb-usb/dw2102.c           |  282 ++++-
 drivers/media/dvb/frontends/Kconfig          |    7 +
 drivers/media/dvb/frontends/Makefile         |    1 +
 drivers/media/dvb/frontends/m88ds3103.c      | 1710 ++++++++++++++++++++++++++
 drivers/media/dvb/frontends/m88ds3103.h      |   53 +
 drivers/media/dvb/frontends/m88ds3103_priv.h |  403 ++++++
 drivers/media/rc/keymaps/Makefile            |    1 +
 drivers/media/rc/keymaps/rc-dvbsky.c         |   78 ++
 drivers/media/video/cx23885/Kconfig          |    1 +
 drivers/media/video/cx23885/cx23885-cards.c  |  163 ++-
 drivers/media/video/cx23885/cx23885-dvb.c    |  100 +-
 drivers/media/video/cx23885/cx23885-f300.c   |   55 +
 drivers/media/video/cx23885/cx23885-f300.h   |    6 +
 drivers/media/video/cx23885/cx23885-input.c  |   18 +
 drivers/media/video/cx23885/cx23885.h        |    6 +-
 drivers/media/video/cx88/Kconfig             |    1 +
 drivers/media/video/cx88/cx88-cards.c        |   22 +
 drivers/media/video/cx88/cx88-dvb.c          |   85 ++
 drivers/media/video/cx88/cx88-input.c        |    4 +
 drivers/media/video/cx88/cx88.h              |    1 +
 include/media/rc-map.h                       |    1 +
 22 files changed, 2995 insertions(+), 4 deletions(-)
 create mode 100644 drivers/media/dvb/frontends/m88ds3103.c
 create mode 100644 drivers/media/dvb/frontends/m88ds3103.h
 create mode 100644 drivers/media/dvb/frontends/m88ds3103_priv.h
 create mode 100644 drivers/media/rc/keymaps/rc-dvbsky.c

diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 63bf456..18bd6fe 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -279,6 +279,7 @@ config DVB_USB_DW2102
 	select DVB_STV0288 if !DVB_FE_CUSTOMISE
 	select DVB_STB6000 if !DVB_FE_CUSTOMISE
 	select DVB_CX24116 if !DVB_FE_CUSTOMISE
+	select DVB_M88DS3103 if !DVB_FE_CUSTOMISE
 	select DVB_SI21XX if !DVB_FE_CUSTOMISE
 	select DVB_TDA10023 if !DVB_FE_CUSTOMISE
 	select DVB_MT312 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 451c5a7..25722c7 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -19,6 +19,7 @@
 #include "stb6000.h"
 #include "eds1547.h"
 #include "cx24116.h"
+#include "m88ds3103.h"
 #include "tda1002x.h"
 #include "mt312.h"
 #include "zl10039.h"
@@ -834,6 +835,39 @@ static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
 	return 0;
 }
 
+static int dvbsky_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+	int i;
+	u8 obuf[] = { 0x1e, 0x00 };
+	u8 ibuf[] = { 0 };
+	struct i2c_msg msg[] = {
+		{
+			.addr = 0x51,
+			.flags = 0,
+			.buf = obuf,
+			.len = 2,
+		}, {
+			.addr = 0x51,
+			.flags = I2C_M_RD,
+			.buf = ibuf,
+			.len = 1,
+
+		}
+	};
+
+	for (i = 0; i < 6; i++) {
+		obuf[1] = i;
+		if (i2c_transfer(&d->i2c_adap, msg, 2) != 2)
+			break;
+		else
+			mac[i] = ibuf[0];
+
+		debug_dump(mac, 6, printk);
+	}
+
+	return 0;
+}
+
 static int su3000_identify_state(struct usb_device *udev,
 				 struct dvb_usb_device_properties *props,
 				 struct dvb_usb_device_description **desc,
@@ -882,6 +916,43 @@ static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 	return 0;
 }
 
+static int bstusb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+
+	struct dvb_usb_adapter *udev_adap =
+		(struct dvb_usb_adapter *)(fe->dvb->priv);
+
+	u8 obuf[3] = { 0xe, 0x80, 0 };
+	u8 ibuf[] = { 0 };
+
+	info("US6830: %s!\n", __func__);
+
+	if (voltage == SEC_VOLTAGE_OFF)
+		obuf[2] = 0;
+	else
+		obuf[2] = 1;
+
+	if (dvb_usb_generic_rw(udev_adap->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	return 0;
+}
+
+static int bstusb_restart(struct dvb_frontend *fe)
+{
+
+	struct dvb_usb_adapter *udev_adap =
+		(struct dvb_usb_adapter *)(fe->dvb->priv);
+
+	u8 obuf[3] = { 0x36, 3, 0 };
+	u8 ibuf[] = { 0 };
+
+	if (dvb_usb_generic_rw(udev_adap->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x36 transfer failed.");
+
+	return 0;
+}
+
 static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon)
 {
 	static u8 led_off[] = { 0 };
@@ -987,6 +1058,24 @@ static struct ds3000_config su3000_ds3000_config = {
 	.ci_mode = 1,
 };
 
+static struct m88ds3103_config US6830_ds3103_config = {
+	.demod_address = 0x68,
+	.ci_mode = 1,
+	.pin_ctrl = 0x83,
+	.ts_mode = 0,
+	.start_ctrl = bstusb_restart,
+	.set_voltage = bstusb_set_voltage,
+};
+
+static struct m88ds3103_config US6832_ds3103_config = {
+	.demod_address = 0x68,
+	.ci_mode = 1,
+	.pin_ctrl = 0x80,
+	.ts_mode = 0,
+	.start_ctrl = bstusb_restart,
+	.set_voltage = bstusb_set_voltage,
+};
+
 static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
 {
 	struct dvb_tuner_ops *tuner_ops = NULL;
@@ -1214,6 +1303,87 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
 	return 0;
 }
 
+static int US6830_frontend_attach(struct dvb_usb_adapter *d)
+{
+	u8 obuf[3] = { 0xe, 0x04, 1 };
+	u8 ibuf[] = { 0 };
+
+	info("US6830: %s!\n", __func__);
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 0;
+	
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	msleep(20);
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 1;
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0x51;
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
+		err("command 0x51 transfer failed.");
+
+	d->fe_adap[0].fe = dvb_attach(m88ds3103_attach, &US6830_ds3103_config,
+					&d->dev->i2c_adap);
+	if (d->fe_adap[0].fe == NULL)
+		return -EIO;
+
+	info("Attached M88DS3103!\n");
+
+	return 0;
+}
+
+static int US6832_frontend_attach(struct dvb_usb_adapter *d)
+{
+	u8 obuf[3] = { 0xe, 0x04, 1 };
+	u8 ibuf[] = { 0 };
+
+	info("US6832: %s!\n", __func__);
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 0;
+	
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	msleep(20);
+	obuf[0] = 0xe;
+	obuf[1] = 0x83;
+	obuf[2] = 1;
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0)
+		err("command 0x0e transfer failed.");
+
+	obuf[0] = 0x51;
+
+	if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0)
+		err("command 0x51 transfer failed.");
+
+	d->fe_adap[0].fe = dvb_attach(m88ds3103_attach, &US6832_ds3103_config,
+					&d->dev->i2c_adap);
+	if (d->fe_adap[0].fe == NULL)
+		return -EIO;
+
+	info("Attached M88DS3103!\n");
+
+	return 0;
+}
+
 static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60,
@@ -1451,6 +1621,9 @@ enum dw2102_table_entry {
 	TEVII_S480_1,
 	TEVII_S480_2,
 	X3M_SPC1400HD,
+	BST_US6830HD,
+	BST_US6831HD,
+	BST_US6832HD,
 };
 
 static struct usb_device_id dw2102_table[] = {
@@ -1469,6 +1642,9 @@ static struct usb_device_id dw2102_table[] = {
 	[TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)},
 	[TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)},
 	[X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)},
+	[BST_US6830HD] = {USB_DEVICE(0x0572, 0x6830)},
+	[BST_US6831HD] = {USB_DEVICE(0x0572, 0x6831)},
+	[BST_US6832HD] = {USB_DEVICE(0x0572, 0x6832)},
 	{ }
 };
 
@@ -1874,6 +2050,106 @@ static struct dvb_usb_device_properties su3000_properties = {
 	}
 };
 
+static struct dvb_usb_device_properties US6830_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.size_of_priv = sizeof(struct su3000_state),
+	.power_ctrl = su3000_power_ctrl,
+	.num_adapters = 1,
+	.identify_state	= su3000_identify_state,
+	.i2c_algo = &su3000_i2c_algo,
+
+	.rc.legacy = {
+		.rc_map_table = rc_map_su3000_table,
+		.rc_map_size = ARRAY_SIZE(rc_map_su3000_table),
+		.rc_interval = 150,
+		.rc_query = dw2102_rc_query,
+	},
+
+	.read_mac_address = dvbsky_read_mac_address,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+	
+	.adapter = {
+		{
+		.num_frontends = 1,
+		.fe = {{
+			.streaming_ctrl   = su3000_streaming_ctrl,
+			.frontend_attach  = US6830_frontend_attach,
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			}
+		}},
+		}
+	},
+	.num_device_descs = 2,
+	.devices = {
+		{ "Bestunar US6830 HD",
+			{ &dw2102_table[BST_US6830HD], NULL },
+			{ NULL },
+		},
+		{ "Bestunar US6831 HD",
+			{ &dw2102_table[BST_US6831HD], NULL },
+			{ NULL },
+		},				
+	}
+};
+
+static struct dvb_usb_device_properties US6832_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.size_of_priv = sizeof(struct su3000_state),
+	.power_ctrl = su3000_power_ctrl,
+	.num_adapters = 1,
+	.identify_state	= su3000_identify_state,
+	.i2c_algo = &su3000_i2c_algo,
+
+	.rc.legacy = {
+		.rc_map_table = rc_map_su3000_table,
+		.rc_map_size = ARRAY_SIZE(rc_map_su3000_table),
+		.rc_interval = 150,
+		.rc_query = dw2102_rc_query,
+	},
+
+	.read_mac_address = dvbsky_read_mac_address,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+
+	.adapter = {
+		{
+		.num_frontends = 1,
+		.fe = {{
+			.streaming_ctrl   = su3000_streaming_ctrl,
+			.frontend_attach  = US6832_frontend_attach,
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			}
+		}},
+		}
+	},
+	.num_device_descs = 1,
+	.devices = {
+		{ "Bestunar US6832 HD",
+			{ &dw2102_table[BST_US6832HD], NULL },
+			{ NULL },
+		},
+	}
+};
+
 static int dw2102_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
@@ -1930,7 +2206,11 @@ static int dw2102_probe(struct usb_interface *intf,
 	    0 == dvb_usb_device_init(intf, p7500,
 			THIS_MODULE, NULL, adapter_nr) ||
 	    0 == dvb_usb_device_init(intf, &su3000_properties,
-				     THIS_MODULE, NULL, adapter_nr))
+     			THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &US6830_properties,
+			THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &US6832_properties,
+			THIS_MODULE, NULL, adapter_nr))
 		return 0;
 
 	return -ENODEV;
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 2124670..eabf9d9 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -214,6 +214,13 @@ config DVB_CX24116
 	help
 	  A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
 
+config DVB_M88DS3103
+	tristate "Montage DS3103 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
 config DVB_SI21XX
 	tristate "Silicon Labs SI21XX based"
 	depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 86fa808..870d5e3 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -98,4 +98,5 @@ obj-$(CONFIG_DVB_A8293) += a8293.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
 obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
 obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
+obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
 
diff --git a/drivers/media/dvb/frontends/m88ds3103.c b/drivers/media/dvb/frontends/m88ds3103.c
new file mode 100644
index 0000000..315809d
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103.c
@@ -0,0 +1,1710 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2011 Max nibble<nibble.max@gmail.com>
+    Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+    Copyright (C) 2009 Konstantin Dimitrov.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "m88ds3103.h"
+#include "m88ds3103_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_INFO "m88ds3103: " args); \
+	} while (0)
+
+/*demod register operations.*/
+static int m88ds3103_writereg(struct m88ds3103_state *state, int reg, int data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address,
+		.flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+	if (debug > 1)
+		printk("m88ds3103: %s: write reg 0x%02x, value 0x%02x\n",
+			__func__, reg, data);
+
+	err = i2c_transfer(state->i2c, &msg, 1);
+	if (err != 1) {
+		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int m88ds3103_readreg(struct m88ds3103_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{ .addr = state->config->demod_address, .flags = 0,
+			.buf = b0, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
+			.buf = b1, .len = 1 }
+	};
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	if (debug > 1)
+		printk(KERN_INFO "m88ds3103: read reg 0x%02x, value 0x%02x\n",
+			reg, b1[0]);
+
+	return b1[0];
+}
+
+/*tuner register operations.*/
+static int m88ds3103_tuner_writereg(struct m88ds3103_state *state, int reg, int data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = 0x60,
+		.flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+	m88ds3103_writereg(state, 0x03, 0x11);
+	err = i2c_transfer(state->i2c, &msg, 1);
+	
+	if (err != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int m88ds3103_tuner_readreg(struct m88ds3103_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{ .addr = 0x60, .flags = 0,
+			.buf = b0, .len = 1 },
+		{ .addr = 0x60, .flags = I2C_M_RD,
+			.buf = b1, .len = 1 }
+	};
+
+	m88ds3103_writereg(state, 0x03, 0x11);	
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+		return ret;
+	}
+
+	return b1[0];
+}
+
+/* Bulk demod I2C write, for firmware download. */
+static int m88ds3103_writeregN(struct m88ds3103_state *state, int reg,
+				const u8 *data, u16 len)
+{
+	int ret = -EREMOTEIO;
+	struct i2c_msg msg;
+	u8 *buf;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (buf == NULL) {
+		printk("Unable to kmalloc\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	*(buf) = reg;
+	memcpy(buf + 1, data, len);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = len + 1;
+
+	if (debug > 1)
+		printk(KERN_INFO "m88ds3103: %s:  write regN 0x%02x, len = %d\n",
+			__func__, reg, len);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+	if (ret != 1) {
+		printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
+			 __func__, ret, reg);
+		ret = -EREMOTEIO;
+	}
+	
+error:
+	kfree(buf);
+
+	return ret;
+}
+
+static int m88ds3103_load_firmware(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	const struct firmware *fw;
+	int i, ret = 0;
+
+	dprintk("%s()\n", __func__);
+		
+	if (state->skip_fw_load)
+		return 0;
+	/* Load firmware */
+	/* request the firmware, this will block until someone uploads it */	
+	if(state->demod_id == DS3000_ID){
+		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+				DS3000_DEFAULT_FIRMWARE);		
+		ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+					state->i2c->dev.parent);
+	}else if(state->demod_id == DS3103_ID){
+		printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+				DS3103_DEFAULT_FIRMWARE);
+		ret = request_firmware(&fw, DS3103_DEFAULT_FIRMWARE,
+					state->i2c->dev.parent);
+	}
+	
+	printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+	if (ret) {
+		printk(KERN_ERR "%s: No firmware uploaded (timeout or file not "
+				"found?)\n", __func__);
+		return ret;
+	}
+
+	/* Make sure we don't recurse back through here during loading */
+	state->skip_fw_load = 1;
+
+	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+			fw->size,
+			fw->data[0],
+			fw->data[1],
+			fw->data[fw->size - 2],
+			fw->data[fw->size - 1]);
+			
+	/* stop internal mcu. */
+	m88ds3103_writereg(state, 0xb2, 0x01);
+	/* split firmware to download.*/
+	for(i = 0; i < FW_DOWN_LOOP; i++){
+		ret = m88ds3103_writeregN(state, 0xb0, &(fw->data[FW_DOWN_SIZE*i]), FW_DOWN_SIZE);
+		if(ret != 1) break;		
+	}
+	/* start internal mcu. */
+	if(ret == 1)
+		m88ds3103_writereg(state, 0xb2, 0x00);
+		
+	release_firmware(fw);
+
+	dprintk("%s: Firmware upload %s\n", __func__,
+			ret == 1 ? "complete" : "failed");
+
+	if(ret == 1) ret = 0;
+	
+	/* Ensure firmware is always loaded if required */
+	state->skip_fw_load = 0;
+
+	return ret;
+}
+
+
+static int m88ds3103_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 data;
+
+	dprintk("%s(%d)\n", __func__, voltage);
+
+	dprintk("m88ds3103:pin_ctrl = (%02x)\n", state->config->pin_ctrl);
+	
+	if(state->config->set_voltage)
+		state->config->set_voltage(fe, voltage);
+	
+	data = m88ds3103_readreg(state, 0xa2);
+	
+        if(state->config->pin_ctrl & 0x80){ /*If control pin is assigned.*/
+	        data &= ~0x03; /* bit0 V/H, bit1 off/on */
+	        if(state->config->pin_ctrl & 0x02)
+		     data |= 0x02;
+
+	        switch (voltage) {
+	        case SEC_VOLTAGE_18:
+		     if((state->config->pin_ctrl & 0x01) == 0)
+			  data |= 0x01;
+		     break;
+	        case SEC_VOLTAGE_13:
+		     if(state->config->pin_ctrl & 0x01)
+			  data |= 0x01;
+		     break;
+	        case SEC_VOLTAGE_OFF:
+		     if(state->config->pin_ctrl & 0x02)
+			   data &= ~0x02;			
+		     else
+			   data |= 0x02;
+		     break;
+	         }
+        }
+
+	m88ds3103_writereg(state, 0xa2, data);
+
+	return 0;
+}
+
+static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t* status)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	int lock = 0;
+	
+	*status = 0;
+	
+	switch (state->delivery_system){
+	case SYS_DVBS:
+		lock = m88ds3103_readreg(state, 0xd1);
+		dprintk("%s: SYS_DVBS status=%x.\n", __func__, lock);
+		
+		if ((lock & 0x07) == 0x07){
+			/*if((m88ds3103_readreg(state, 0x0d) & 0x07) == 0x07)*/
+				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER 
+					| FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+			
+		}
+		break;
+	case SYS_DVBS2:
+		lock = m88ds3103_readreg(state, 0x0d);
+		dprintk("%s: SYS_DVBS2 status=%x.\n", __func__, lock);
+
+		if ((lock & 0x8f) == 0x8f)
+			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER 
+				| FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+			
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int m88ds3103_read_ber(struct dvb_frontend *fe, u32* ber)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp1, tmp2, tmp3;
+	u32 ldpc_frame_cnt, pre_err_packags, code_rate_fac = 0;
+
+	dprintk("%s()\n", __func__);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		m88ds3103_writereg(state, 0xf9, 0x04);
+		tmp3 = m88ds3103_readreg(state, 0xf8);
+		if ((tmp3&0x10) == 0){
+			tmp1 = m88ds3103_readreg(state, 0xf7);
+			tmp2 = m88ds3103_readreg(state, 0xf6);
+			tmp3 |= 0x10;
+			m88ds3103_writereg(state, 0xf8, tmp3);
+			state->preBer = (tmp1<<8) | tmp2;
+		}
+		break;
+	case SYS_DVBS2:
+		tmp1 = m88ds3103_readreg(state, 0x7e) & 0x0f;
+		switch(tmp1){
+		case 0:	code_rate_fac = 16008 - 80; break;
+		case 1:	code_rate_fac = 21408 - 80; break;
+		case 2:	code_rate_fac = 25728 - 80; break;
+		case 3:	code_rate_fac = 32208 - 80; break;
+		case 4:	code_rate_fac = 38688 - 80; break;
+		case 5:	code_rate_fac = 43040 - 80; break;
+		case 6:	code_rate_fac = 48408 - 80; break;
+		case 7:	code_rate_fac = 51648 - 80; break;
+		case 8:	code_rate_fac = 53840 - 80; break;
+		case 9:	code_rate_fac = 57472 - 80; break;
+		case 10: code_rate_fac = 58192 - 80; break;
+		}
+		
+		tmp1 = m88ds3103_readreg(state, 0xd7) & 0xff;
+		tmp2 = m88ds3103_readreg(state, 0xd6) & 0xff;
+		tmp3 = m88ds3103_readreg(state, 0xd5) & 0xff;		
+		ldpc_frame_cnt = (tmp1 << 16) | (tmp2 << 8) | tmp3;
+
+		tmp1 = m88ds3103_readreg(state, 0xf8) & 0xff;
+		tmp2 = m88ds3103_readreg(state, 0xf7) & 0xff;
+		pre_err_packags = tmp1<<8 | tmp2;
+		
+		if (ldpc_frame_cnt > 1000){
+			m88ds3103_writereg(state, 0xd1, 0x01);
+			m88ds3103_writereg(state, 0xf9, 0x01);
+			m88ds3103_writereg(state, 0xf9, 0x00);
+			m88ds3103_writereg(state, 0xd1, 0x00);
+			state->preBer = pre_err_packags;
+		} 				
+		break;
+	default:
+		break;
+	}
+	*ber = state->preBer;
+	
+	return 0;
+}
+
+static int m88ds3103_read_signal_strength(struct dvb_frontend *fe,
+						u16 *signal_strength)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u16 gain;
+	u8 gain1, gain2, gain3 = 0;
+
+	dprintk("%s()\n", __func__);
+
+	gain1 = m88ds3103_tuner_readreg(state, 0x3d) & 0x1f;
+	dprintk("%s: gain1 = 0x%02x \n", __func__, gain1);
+	
+	if (gain1 > 15) gain1 = 15;
+	gain2 = m88ds3103_tuner_readreg(state, 0x21) & 0x1f;
+	dprintk("%s: gain2 = 0x%02x \n", __func__, gain2);
+	
+	if(state->tuner_id == TS2022_ID){
+		gain3 = (m88ds3103_tuner_readreg(state, 0x66)>>3) & 0x07;
+		dprintk("%s: gain3 = 0x%02x \n", __func__, gain3);
+		
+		if (gain2 > 16) gain2 = 16;
+		if (gain2 < 2) gain2 = 2;			
+		if (gain3 > 6) gain3 = 6;
+	}else{
+		if (gain2 > 13) gain2 = 13;
+		gain3 = 0;
+	}
+
+	gain = gain1*23 + gain2*35 + gain3*29;
+	*signal_strength = 60000 - gain*55;
+
+	return 0;
+}
+
+
+static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *p_snr)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 val, npow1, npow2, spow1, cnt;
+	u16 tmp, snr;
+	u32 npow, spow, snr_total;	
+	static const u16 mes_log10[] ={
+		0,	3010,	4771,	6021, 	6990,	7781,	8451,	9031,	9542,	10000,
+		10414,	10792,	11139,	11461,	11761,	12041,	12304,	12553,	12788,	13010,
+		13222,	13424,	13617,	13802,	13979,	14150,	14314,	14472,	14624,	14771,
+		14914,	15052,	15185,	15315,	15441,	15563,	15682,	15798,	15911,	16021,
+		16128,	16232,	16335,	16435,	16532,	16628,	16721,	16812,	16902,	16990,
+		17076,	17160,	17243,	17324,	17404,	17482,	17559,	17634,	17709,	17782,
+		17853,	17924,	17993,	18062,	18129,	18195,	18261,	18325,	18388,	18451,
+		18513,	18573,	18633,	18692,	18751,	18808,	18865,	18921,	18976,	19031
+	};
+	static const u16 mes_loge[] ={
+		0,	6931,	10986,	13863, 	16094,	17918,	19459,	20794,	21972,	23026,
+		23979,	24849,	25649,	26391,	27081,	27726,	28332,	28904,	29444,	29957,
+		30445,	30910,	31355,	31781,	32189,	32581,	32958,	33322,	33673,	34012,
+		34340,	34657,
+	};
+
+	dprintk("%s()\n", __func__);
+
+	snr = 0;
+	
+	switch (state->delivery_system){
+	case SYS_DVBS:
+		cnt = 10; snr_total = 0;
+		while(cnt > 0){
+			val = m88ds3103_readreg(state, 0xff);
+			snr_total += val;
+			cnt--;
+		}
+		tmp = (u16)(snr_total/80);
+		if(tmp > 0){
+			if (tmp > 32) tmp = 32;
+			snr = (mes_loge[tmp - 1] * 100) / 45;
+		}else{
+			snr = 0;
+		}
+		break;
+	case SYS_DVBS2:
+		cnt  = 10; npow = 0; spow = 0;
+		while(cnt >0){
+			npow1 = m88ds3103_readreg(state, 0x8c) & 0xff;
+			npow2 = m88ds3103_readreg(state, 0x8d) & 0xff;
+			npow += (((npow1 & 0x3f) + (u16)(npow2 << 6)) >> 2);
+
+			spow1 = m88ds3103_readreg(state, 0x8e) & 0xff;
+			spow += ((spow1 * spow1) >> 1);
+			cnt--;
+		}
+		npow /= 10; spow /= 10;
+		if(spow == 0){
+			snr = 0;
+		}else if(npow == 0){
+			snr = 19;
+		}else{
+			if(spow > npow){
+				tmp = (u16)(spow / npow);
+				if (tmp > 80) tmp = 80;
+				snr = mes_log10[tmp - 1]*3;
+			}else{
+				tmp = (u16)(npow / spow);
+				if (tmp > 80) tmp = 80;
+				snr = -(mes_log10[tmp - 1] / 1000);
+			}
+		}			
+		break;
+	default:
+		break;
+	}
+	*p_snr = snr;
+
+	return 0;
+}
+
+
+static int m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp1, tmp2, tmp3, data;
+
+	dprintk("%s()\n", __func__);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		data = m88ds3103_readreg(state, 0xf8);
+		data |= 0x40;
+		m88ds3103_writereg(state, 0xf8, data);		
+		tmp1 = m88ds3103_readreg(state, 0xf5);
+		tmp2 = m88ds3103_readreg(state, 0xf4);
+		*ucblocks = (tmp1 <<8) | tmp2;		
+		data &= ~0x20;
+		m88ds3103_writereg(state, 0xf8, data);
+		data |= 0x20;
+		m88ds3103_writereg(state, 0xf8, data);
+		data &= ~0x40;
+		m88ds3103_writereg(state, 0xf8, data);
+		break;
+	case SYS_DVBS2:
+		tmp1 = m88ds3103_readreg(state, 0xda);
+		tmp2 = m88ds3103_readreg(state, 0xd9);
+		tmp3 = m88ds3103_readreg(state, 0xd8);
+		*ucblocks = (tmp1 <<16)|(tmp2 <<8)|tmp3;
+		data = m88ds3103_readreg(state, 0xd1);
+		data |= 0x01;
+		m88ds3103_writereg(state, 0xd1, data);
+		data &= ~0x01;
+		m88ds3103_writereg(state, 0xd1, data);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int m88ds3103_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 data_a1, data_a2;
+
+	dprintk("%s(%d)\n", __func__, tone);
+	if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+		printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+		return -EINVAL;
+	}
+
+	data_a1 = m88ds3103_readreg(state, 0xa1);
+	data_a2 = m88ds3103_readreg(state, 0xa2);
+	if(state->demod_id == DS3103_ID)
+		data_a2 &= 0xdf; /* Normal mode */
+	switch (tone) {
+	case SEC_TONE_ON:
+		dprintk("%s: SEC_TONE_ON\n", __func__);
+		data_a1 |= 0x04;
+		data_a1 &= ~0x03;
+		data_a1 &= ~0x40;
+		data_a2 &= ~0xc0;
+		break;
+	case SEC_TONE_OFF:
+		dprintk("%s: SEC_TONE_OFF\n", __func__);
+		data_a2 &= ~0xc0;
+		data_a2 |= 0x80;
+		break;
+	}
+	m88ds3103_writereg(state, 0xa2, data_a2);
+	m88ds3103_writereg(state, 0xa1, data_a1);
+	return 0;
+}
+
+static int m88ds3103_send_diseqc_msg(struct dvb_frontend *fe,
+				struct dvb_diseqc_master_cmd *d)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	int i, ret = 0;
+	u8 tmp, time_out;
+
+	/* Dump DiSEqC message */
+	if (debug) {
+		printk(KERN_INFO "m88ds3103: %s(", __func__);
+		for (i = 0 ; i < d->msg_len ;) {
+			printk(KERN_INFO "0x%02x", d->msg[i]);
+			if (++i < d->msg_len)
+				printk(KERN_INFO ", ");
+		}
+	}
+
+	tmp = m88ds3103_readreg(state, 0xa2);
+	tmp &= ~0xc0;
+	if(state->demod_id == DS3103_ID)
+		tmp &= ~0x20;
+	m88ds3103_writereg(state, 0xa2, tmp);
+	
+	for (i = 0; i < d->msg_len; i ++)
+		m88ds3103_writereg(state, (0xa3+i), d->msg[i]);
+
+	tmp = m88ds3103_readreg(state, 0xa1);	
+	tmp &= ~0x38;
+	tmp &= ~0x40;
+	tmp |= ((d->msg_len-1) << 3) | 0x07;
+	tmp &= ~0x80;
+	m88ds3103_writereg(state, 0xa1, tmp);
+	/*	1.5 * 9 * 8	= 108ms	*/
+	time_out = 150;
+	while (time_out > 0){
+		msleep(10);
+		time_out -= 10;
+		tmp = m88ds3103_readreg(state, 0xa1);		
+		if ((tmp & 0x40) == 0)
+			break;
+	}
+	if (time_out == 0){
+		tmp = m88ds3103_readreg(state, 0xa1);
+		tmp &= ~0x80;
+		tmp |= 0x40;
+		m88ds3103_writereg(state, 0xa1, tmp);
+		ret = 1;
+	}
+	tmp = m88ds3103_readreg(state, 0xa2);
+	tmp &= ~0xc0;
+	tmp |= 0x80;
+	m88ds3103_writereg(state, 0xa2, tmp);	
+	return ret;
+}
+
+
+static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+					fe_sec_mini_cmd_t burst)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8	val, time_out;
+	
+	dprintk("%s()\n", __func__);
+
+	val = m88ds3103_readreg(state, 0xa2);
+	val &= ~0xc0;
+	if(state->demod_id == DS3103_ID)
+		val &= 0xdf; /* Normal mode */
+	m88ds3103_writereg(state, 0xa2, val);
+	/* DiSEqC burst */
+	if (burst == SEC_MINI_B)
+		m88ds3103_writereg(state, 0xa1, 0x01);
+	else
+		m88ds3103_writereg(state, 0xa1, 0x02);
+
+	msleep(13);
+
+	time_out = 5;
+	do{
+		val = m88ds3103_readreg(state, 0xa1);
+		if ((val & 0x40) == 0)
+			break;
+		msleep(1);
+		time_out --;
+	} while (time_out > 0);
+
+	val = m88ds3103_readreg(state, 0xa2);
+	val &= ~0xc0;
+	val |= 0x80;
+	m88ds3103_writereg(state, 0xa2, val);
+	
+	return 0;
+}
+
+static void m88ds3103_release(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+	kfree(state);
+}
+
+static int m88ds3103_check_id(struct m88ds3103_state *state)
+{
+	int val_00, val_01;
+	
+	/*check demod id*/
+	val_01 = m88ds3103_readreg(state, 0x01);
+	printk(KERN_INFO "DS3000 chip version: %x attached.\n", val_01);
+			
+	if(val_01 == 0xD0)
+		state->demod_id = DS3103_ID;
+	else if(val_01 == 0xC0)
+		state->demod_id = DS3000_ID;
+	else
+		state->demod_id = UNKNOW_ID;
+		
+	/*check tuner id*/
+	val_00 = m88ds3103_tuner_readreg(state, 0x00);
+	printk(KERN_INFO "TS202x chip version[1]: %x attached.\n", val_00);
+	val_00 &= 0x03;
+	if(val_00 == 0)
+	{
+		m88ds3103_tuner_writereg(state, 0x00, 0x01);
+		msleep(3);		
+	}
+	m88ds3103_tuner_writereg(state, 0x00, 0x03);
+	msleep(5);
+	
+	val_00 = m88ds3103_tuner_readreg(state, 0x00);
+	printk(KERN_INFO "TS202x chip version[2]: %x attached.\n", val_00);
+	val_00 &= 0xff;
+	if((val_00 == 0x01) || (val_00 == 0x41) || (val_00 == 0x81))
+		state->tuner_id = TS2020_ID;
+	else if(((val_00 & 0xc0)== 0xc0) || (val_00 == 0x83))
+		state->tuner_id = TS2022_ID;
+	else
+		state->tuner_id = UNKNOW_ID;
+			
+	return state->demod_id;	
+}
+
+static struct dvb_frontend_ops m88ds3103_ops;
+static int m88ds3103_initilaze(struct dvb_frontend *fe);
+
+struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *config,
+				    struct i2c_adapter *i2c)
+{
+	struct m88ds3103_state *state = NULL;
+
+	dprintk("%s\n", __func__);
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct m88ds3103_state), GFP_KERNEL);
+	if (state == NULL) {
+		printk(KERN_ERR "Unable to kmalloc\n");
+		goto error2;
+	}
+
+	state->config = config;
+	state->i2c = i2c;
+	state->preBer = 0xffff;
+	state->delivery_system = SYS_DVBS; /*Default to DVB-S.*/
+	
+	/* check demod id */
+	if(m88ds3103_check_id(state) == UNKNOW_ID){
+		printk(KERN_ERR "Unable to find Montage chip\n");
+		goto error3;
+	}
+
+	memcpy(&state->frontend.ops, &m88ds3103_ops,
+			sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	
+	m88ds3103_initilaze(&state->frontend);
+	
+	return &state->frontend;
+
+error3:
+	kfree(state);
+error2:
+	return NULL;
+}
+EXPORT_SYMBOL(m88ds3103_attach);
+
+static int m88ds3103_set_carrier_offset(struct dvb_frontend *fe,
+					s32 carrier_offset_khz)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	s32 tmp;
+
+	tmp = carrier_offset_khz;
+	tmp *= 65536;
+	
+	tmp = (2*tmp + MT_FE_MCLK_KHZ) / (2*MT_FE_MCLK_KHZ);
+
+	if (tmp < 0)
+		tmp += 65536;
+
+	m88ds3103_writereg(state, 0x5f, tmp >> 8);
+	m88ds3103_writereg(state, 0x5e, tmp & 0xff);
+
+	return 0;
+}
+
+static int m88ds3103_set_symrate(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u16 value;
+	
+	value = (((c->symbol_rate / 1000) << 15) + (MT_FE_MCLK_KHZ / 4)) / (MT_FE_MCLK_KHZ / 2);
+	m88ds3103_writereg(state, 0x61, value & 0x00ff);
+	m88ds3103_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+	return 0;
+}
+
+static int m88ds3103_set_CCI(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 tmp;
+
+	tmp = m88ds3103_readreg(state, 0x56);
+	tmp &= ~0x01;
+	m88ds3103_writereg(state, 0x56, tmp);
+
+	tmp = m88ds3103_readreg(state, 0x76);
+	tmp &= ~0x80;
+	m88ds3103_writereg(state, 0x76, tmp);
+
+	return 0;
+}
+
+static int m88ds3103_init_reg(struct m88ds3103_state *state, const u8 *p_reg_tab, u32 size)
+{
+	u32 i;
+	
+	for(i = 0; i < size; i+=2)
+		m88ds3103_writereg(state, p_reg_tab[i], p_reg_tab[i+1]);
+		
+	return 0;
+}
+
+static int m88ds3103_get_locked_sym_rate(struct m88ds3103_state *state, u32 *sym_rate_KSs)
+{
+	u16	tmp;
+	u32	sym_rate_tmp;
+	u8	val_0x6d, val_0x6e;
+
+	val_0x6d = m88ds3103_readreg(state, 0x6d);
+	val_0x6e = m88ds3103_readreg(state, 0x6e);
+
+	tmp = (u16)((val_0x6e<<8) | val_0x6d);
+
+	sym_rate_tmp = (u32)(tmp * MT_FE_MCLK_KHZ);
+	sym_rate_tmp = (u32)(sym_rate_tmp / (1<<16));
+	*sym_rate_KSs = sym_rate_tmp;
+
+	return 0;
+}
+
+static int m88ds3103_get_channel_info(struct m88ds3103_state *state, u8 *p_mode, u8 *p_coderate)
+{
+	u8	tmp, val_0x7E;
+
+	if(state->delivery_system == SYS_DVBS2){
+		val_0x7E = m88ds3103_readreg(state, 0x7e);
+		tmp = (u8)((val_0x7E&0xC0) >> 6);
+		*p_mode = tmp;
+		tmp = (u8)(val_0x7E & 0x0f);
+		*p_coderate = tmp;
+	} else {
+		*p_mode = 0;
+		tmp = m88ds3103_readreg(state, 0xe6);		
+		tmp = (u8)(tmp >> 5);
+		*p_coderate = tmp;
+	}
+	
+	return 0;
+}
+
+static int m88ds3103_set_clock_ratio(struct m88ds3103_state *state)
+{
+	u8	val, mod_fac, tmp1, tmp2;
+	u32	input_datarate, locked_sym_rate_KSs;
+	u32 MClk_KHz = 96000;
+	u8 mod_mode, code_rate, divid_ratio = 0;
+
+	locked_sym_rate_KSs = 0;
+	m88ds3103_get_locked_sym_rate(state, &locked_sym_rate_KSs);
+	if(locked_sym_rate_KSs == 0)
+		return 0;
+
+	m88ds3103_get_channel_info(state, &mod_mode, &code_rate);
+
+	if (state->delivery_system == SYS_DVBS2)
+	{
+		switch(mod_mode) {
+			case 1: mod_fac = 3; break;
+			case 2:	mod_fac = 4; break;
+			case 3:	mod_fac = 5; break;
+			default: mod_fac = 2; break;
+		}
+
+		switch(code_rate) {
+			case 0: input_datarate = locked_sym_rate_KSs*mod_fac/8/4; break;
+			case 1: input_datarate = locked_sym_rate_KSs*mod_fac/8/3;  	break;
+			case 2: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/5;	break;
+			case 3:	input_datarate = locked_sym_rate_KSs*mod_fac/8/2;	break;
+			case 4:	input_datarate = locked_sym_rate_KSs*mod_fac*3/8/5;	break;
+			case 5:	input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3;	break;
+			case 6:	input_datarate = locked_sym_rate_KSs*mod_fac*3/8/4;	break;
+			case 7:	input_datarate = locked_sym_rate_KSs*mod_fac*4/8/5;	break;
+			case 8:	input_datarate = locked_sym_rate_KSs*mod_fac*5/8/6;	break;
+			case 9:	input_datarate = locked_sym_rate_KSs*mod_fac*8/8/9;	break;
+			case 10: input_datarate = locked_sym_rate_KSs*mod_fac*9/8/10; break;
+			default: input_datarate = locked_sym_rate_KSs*mod_fac*2/8/3; break;
+		}
+
+		if(state->demod_id == DS3000_ID)
+			input_datarate = input_datarate * 115 / 100;
+
+		if(input_datarate < 4800)  {tmp1 = 15;tmp2 = 15;} //4.8MHz         TS clock
+		else if(input_datarate < 4966)  {tmp1 = 14;tmp2 = 15;} //4.966MHz  TS clock
+		else if(input_datarate < 5143)  {tmp1 = 14;tmp2 = 14;} //5.143MHz  TS clock
+		else if(input_datarate < 5333)  {tmp1 = 13;tmp2 = 14;} //5.333MHz  TS clock
+		else if(input_datarate < 5538)  {tmp1 = 13;tmp2 = 13;} //5.538MHz  TS clock
+		else if(input_datarate < 5760)  {tmp1 = 12;tmp2 = 13;} //5.76MHz   TS clock       allan 0809
+		else if(input_datarate < 6000)  {tmp1 = 12;tmp2 = 12;} //6MHz      TS clock
+		else if(input_datarate < 6260)  {tmp1 = 11;tmp2 = 12;} //6.26MHz   TS clock
+		else if(input_datarate < 6545)  {tmp1 = 11;tmp2 = 11;} //6.545MHz  TS clock
+		else if(input_datarate < 6857)  {tmp1 = 10;tmp2 = 11;} //6.857MHz  TS clock
+		else if(input_datarate < 7200)  {tmp1 = 10;tmp2 = 10;} //7.2MHz    TS clock
+		else if(input_datarate < 7578)  {tmp1 = 9;tmp2 = 10;}  //7.578MHz  TS clock
+		else if(input_datarate < 8000)	{tmp1 = 9;tmp2 = 9;}   //8MHz      TS clock
+		else if(input_datarate < 8470)	{tmp1 = 8;tmp2 = 9;}   //8.47MHz   TS clock
+		else if(input_datarate < 9000)	{tmp1 = 8;tmp2 = 8;}   //9MHz      TS clock
+		else if(input_datarate < 9600)	{tmp1 = 7;tmp2 = 8;}   //9.6MHz    TS clock
+		else if(input_datarate < 10285)	{tmp1 = 7;tmp2 = 7;}   //10.285MHz TS clock
+		else if(input_datarate < 12000)	{tmp1 = 6;tmp2 = 6;}   //12MHz     TS clock
+		else if(input_datarate < 14400)	{tmp1 = 5;tmp2 = 5;}   //14.4MHz   TS clock
+		else if(input_datarate < 18000)	{tmp1 = 4;tmp2 = 4;}   //18MHz     TS clock
+		else							{tmp1 = 3;tmp2 = 3;}   //24MHz     TS clock
+
+		if(state->demod_id == DS3000_ID) {
+			val = (u8)((tmp1<<4) + tmp2);
+			m88ds3103_writereg(state, 0xfe, val);
+		} else {
+			tmp1 = m88ds3103_readreg(state, 0x22);
+			tmp2 = m88ds3103_readreg(state, 0x24);
+
+			tmp1 >>= 6;
+			tmp1 &= 0x03;
+			tmp2 >>= 6;
+			tmp2 &= 0x03;
+
+			if((tmp1 == 0x00) && (tmp2 == 0x01))
+				MClk_KHz = 144000;
+			else if((tmp1 == 0x00) && (tmp2 == 0x03))
+				MClk_KHz = 72000;
+			else if((tmp1 == 0x01) && (tmp2 == 0x01))
+				MClk_KHz = 115200;
+			else if((tmp1 == 0x02) && (tmp2 == 0x01))
+				MClk_KHz = 96000;
+			else if((tmp1 == 0x03) && (tmp2 == 0x00))
+				MClk_KHz = 192000;
+			else
+				return 0;
+
+			if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/
+				input_datarate = 5200;
+				
+			if(input_datarate != 0)
+				divid_ratio = (u8)(MClk_KHz / input_datarate);
+			else
+				divid_ratio = 0xFF;
+
+			if(divid_ratio > 128)
+				divid_ratio = 128;
+
+			if(divid_ratio < 2)
+				divid_ratio = 2;
+
+			tmp1 = (u8)(divid_ratio / 2);
+			tmp2 = (u8)(divid_ratio / 2);
+
+			if((divid_ratio % 2) != 0)
+				tmp2 += 1;
+
+			tmp1 -= 1;
+			tmp2 -= 1;
+
+			tmp1 &= 0x3f;
+			tmp2 &= 0x3f;
+
+			val = m88ds3103_readreg(state, 0xfe);
+			val &= 0xF0;
+			val |= (tmp2 >> 2) & 0x0f;
+			m88ds3103_writereg(state, 0xfe, val);
+
+			val = (u8)((tmp2 & 0x03) << 6);	
+			val |= tmp1;
+			m88ds3103_writereg(state, 0xea, val);
+		}
+	} else {
+		mod_fac = 2;
+
+		switch(code_rate) {
+			case 4:	input_datarate = locked_sym_rate_KSs*mod_fac/2/8;	break;
+			case 3:	input_datarate = locked_sym_rate_KSs*mod_fac*2/3/8;	break;
+			case 2:	input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8;	break;
+			case 1:	input_datarate = locked_sym_rate_KSs*mod_fac*5/6/8;	break;
+			case 0:	input_datarate = locked_sym_rate_KSs*mod_fac*7/8/8;	break;
+			default: input_datarate = locked_sym_rate_KSs*mod_fac*3/4/8;	break;
+		}
+
+		if(state->demod_id == DS3000_ID)
+			input_datarate = input_datarate * 115 / 100;
+
+		if(input_datarate < 6857)		{tmp1 = 7;tmp2 = 7;} //6.857MHz     TS clock
+		else if(input_datarate < 7384)	{tmp1 = 6;tmp2 = 7;} //7.384MHz     TS clock
+		else if(input_datarate < 8000)	{tmp1 = 6;tmp2 = 6;} //8MHz     	TS clock
+		else if(input_datarate < 8727)	{tmp1 = 5;tmp2 = 6;} //8.727MHz 	TS clock
+		else if(input_datarate < 9600)	{tmp1 = 5;tmp2 = 5;} //9.6MHz    	TS clock
+		else if(input_datarate < 10666)	{tmp1 = 4;tmp2 = 5;} //10.666MHz 	TS clock
+		else if(input_datarate < 12000)	{tmp1 = 4;tmp2 = 4;} //12MHz 	 	TS clock
+		else if(input_datarate < 13714)	{tmp1 = 3;tmp2 = 4;} //13.714MHz 	TS clock
+		else if(input_datarate < 16000)	{tmp1 = 3;tmp2 = 3;} //16MHz     	TS clock
+		else if(input_datarate < 19200)	{tmp1 = 2;tmp2 = 3;} //19.2MHz   	TS clock
+		else 							{tmp1 = 2;tmp2 = 2;} //24MHz     	TS clock
+
+		if(state->demod_id == DS3000_ID) {
+			val = m88ds3103_readreg(state, 0xfe);
+			val &= 0xc0;
+			val |= ((u8)((tmp1<<3) + tmp2));
+			m88ds3103_writereg(state, 0xfe, val);
+		} else {
+			if(input_datarate < 5200) /*Max. 2011-12-23 11:55*/
+				input_datarate = 5200;
+			
+			if(input_datarate != 0)
+				divid_ratio = (u8)(MClk_KHz / input_datarate);
+			else
+				divid_ratio = 0xFF;
+
+			if(divid_ratio > 128)
+				divid_ratio = 128;
+
+			if(divid_ratio < 2)
+				divid_ratio = 2;
+
+			tmp1 = (u8)(divid_ratio / 2);
+			tmp2 = (u8)(divid_ratio / 2);
+
+			if((divid_ratio % 2) != 0)
+				tmp2 += 1;
+
+			tmp1 -= 1;
+			tmp2 -= 1;
+
+			tmp1 &= 0x3f;
+			tmp2 &= 0x3f;
+
+			val = m88ds3103_readreg(state, 0xfe);
+			val &= 0xF0;
+			val |= (tmp2 >> 2) & 0x0f;
+			m88ds3103_writereg(state, 0xfe, val);
+			
+			val = (u8)((tmp2 & 0x03) << 6);
+			val |= tmp1;
+			m88ds3103_writereg(state, 0xea, val);
+		}
+	}
+	return 0;
+}
+
+static int m88ds3103_demod_connect(struct dvb_frontend *fe, s32 carrier_offset_khz) 
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	u16 value;
+	u8 val1,val2,data;
+	
+	dprintk("connect delivery system = %d\n", state->delivery_system);
+	
+	/* ds3000 global reset */
+	m88ds3103_writereg(state, 0x07, 0x80);
+	m88ds3103_writereg(state, 0x07, 0x00);
+	/* ds3000 build-in uC reset */
+	m88ds3103_writereg(state, 0xb2, 0x01);
+	/* ds3000 software reset */
+	m88ds3103_writereg(state, 0x00, 0x01);
+
+	switch (state->delivery_system) {
+	case SYS_DVBS:
+		/* initialise the demod in DVB-S mode */
+		if(state->demod_id == DS3000_ID){
+			m88ds3103_init_reg(state, ds3000_dvbs_init_tab, sizeof(ds3000_dvbs_init_tab));
+			
+			value = m88ds3103_readreg(state, 0xfe);
+			value &= 0xc0;
+			value |= 0x1b;
+			m88ds3103_writereg(state, 0xfe, value);
+			
+			if(state->config->ci_mode)
+				val1 = 0x80;
+			else if(state->config->ts_mode)
+				val1 = 0x60;
+			else
+				val1 = 0x20;
+			m88ds3103_writereg(state, 0xfd, val1);
+			
+		}else if(state->demod_id == DS3103_ID){
+			m88ds3103_init_reg(state, ds3103_dvbs_init_tab, sizeof(ds3103_dvbs_init_tab));
+			
+			/* set ts clock */
+			if(state->config->ci_mode == 2){
+				val1 = 6; val2 = 6;
+			}else if(state->config->ts_mode == 0)	{
+				val1 = 3; val2 = 3;
+			}else{
+				val1 = 0; val2 = 0;
+			}
+			val1 -= 1; val2 -= 1;
+			val1 &= 0x3f; val2 &= 0x3f;
+			data = m88ds3103_readreg(state, 0xfe);
+			data &= 0xf0;
+			data |= (val2 >> 2) & 0x0f;
+			m88ds3103_writereg(state, 0xfe, data);
+			data = (val2 & 0x03) << 6;
+			data |= val1;
+			m88ds3103_writereg(state, 0xea, data);
+			
+			m88ds3103_writereg(state, 0x4d, 0xfd & m88ds3103_readreg(state, 0x4d));
+			m88ds3103_writereg(state, 0x30, 0xef & m88ds3103_readreg(state, 0x30));
+			
+			/* set master clock */
+			val1 = m88ds3103_readreg(state, 0x22);
+			val2 = m88ds3103_readreg(state, 0x24);
+			
+			val1 &= 0x3f;
+			val2 &= 0x3f;
+			val1 |= 0x80;
+			val2 |= 0x40;
+
+			m88ds3103_writereg(state, 0x22, val1);
+			m88ds3103_writereg(state, 0x24, val2);	
+			
+			if(state->config->ci_mode)
+				val1 = 0x03;
+			else if(state->config->ts_mode)
+				val1 = 0x06;
+			else
+				val1 = 0x42;
+			m88ds3103_writereg(state, 0xfd, val1);		
+		}
+		break;
+	case SYS_DVBS2:
+		/* initialise the demod in DVB-S2 mode */
+		if(state->demod_id == DS3000_ID){
+			m88ds3103_init_reg(state, ds3000_dvbs2_init_tab, sizeof(ds3000_dvbs2_init_tab));
+		
+			if (c->symbol_rate >= 30000000)
+				m88ds3103_writereg(state, 0xfe, 0x54);
+			else
+				m88ds3103_writereg(state, 0xfe, 0x98);
+								
+		}else if(state->demod_id == DS3103_ID){
+			m88ds3103_init_reg(state, ds3103_dvbs2_init_tab, sizeof(ds3103_dvbs2_init_tab));
+
+			/* set ts clock */
+			if(state->config->ci_mode == 2){
+				val1 = 6; val2 = 6;
+			}else if(state->config->ts_mode == 0){
+				val1 = 5; val2 = 4;
+			}else{
+				val1 = 0; val2 = 0;
+			}
+			val1 -= 1; val2 -= 1;
+			val1 &= 0x3f; val2 &= 0x3f;
+			data = m88ds3103_readreg(state, 0xfe);
+			data &= 0xf0;
+			data |= (val2 >> 2) & 0x0f;
+			m88ds3103_writereg(state, 0xfe, data);
+			data = (val2 & 0x03) << 6;
+			data |= val1;
+			m88ds3103_writereg(state, 0xea, data);
+			
+			m88ds3103_writereg(state, 0x4d, 0xfd & m88ds3103_readreg(state, 0x4d));
+			m88ds3103_writereg(state, 0x30, 0xef & m88ds3103_readreg(state, 0x30));
+			
+			/* set master clock */
+			val1 = m88ds3103_readreg(state, 0x22);
+			val2 = m88ds3103_readreg(state, 0x24);
+			
+			val1 &= 0x3f;
+			val2 &= 0x3f;
+			if((state->config->ci_mode == 2) || (state->config->ts_mode == 1)){
+				val1 |= 0x80;
+				val2 |= 0x40;
+			}else{
+				if (c->symbol_rate >= 28000000){
+					val1 |= 0xc0;
+				}else if (c->symbol_rate >= 18000000){
+					val2 |= 0x40;
+				}else{
+					val1 |= 0x80;
+					val2 |= 0x40;
+				}				
+			}
+			m88ds3103_writereg(state, 0x22, val1);
+			m88ds3103_writereg(state, 0x24, val2);					
+		}
+		
+		if(state->config->ci_mode)
+			val1 = 0x03;
+		else if(state->config->ts_mode)
+			val1 = 0x06;
+		else
+			val1 = 0x42;
+		m88ds3103_writereg(state, 0xfd, val1);
+		
+		break;
+	default:
+		return 1;
+	}
+	/* disable 27MHz clock output */
+	m88ds3103_writereg(state, 0x29, 0x80);
+	/* enable ac coupling */
+	m88ds3103_writereg(state, 0x25, 0x8a);
+
+	if ((c->symbol_rate / 1000) <= 3000){
+		m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 32 * 100 / 64 = 400*/
+		m88ds3103_writereg(state, 0xc8, 0x20);
+		m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		m88ds3103_writereg(state, 0xc7, 0x00);
+	}else if((c->symbol_rate / 1000) <= 10000){
+		m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 16 * 100 / 64 = 200*/
+		m88ds3103_writereg(state, 0xc8, 0x10);
+		m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		m88ds3103_writereg(state, 0xc7, 0x00);
+	}else{
+		m88ds3103_writereg(state, 0xc3, 0x08); /* 8 * 6 * 100 / 64 = 75*/
+		m88ds3103_writereg(state, 0xc8, 0x06);
+		m88ds3103_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
+		m88ds3103_writereg(state, 0xc7, 0x00);
+	}
+
+	m88ds3103_set_symrate(fe);
+	
+	m88ds3103_set_CCI(fe);
+
+	m88ds3103_set_carrier_offset(fe, carrier_offset_khz);
+		
+	/* ds3000 out of software reset */
+	m88ds3103_writereg(state, 0x00, 0x00);
+	/* start ds3000 build-in uC */
+	m88ds3103_writereg(state, 0xb2, 0x00);	
+	
+	return 0;
+}
+
+static int m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	int i;
+	fe_status_t status;
+	u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf, div4, capCode, changePLL;
+	s32 offset_khz, lpf_offset_KHz;
+	u16 value, ndiv, lpf_coeff;
+	u32 f3db, gdiv28, realFreq;
+	u8 RFgain;
+
+	dprintk("%s() ", __func__);
+	dprintk("c frequency = %d\n", c->frequency);
+	dprintk("symbol rate = %d\n", c->symbol_rate);
+	dprintk("delivery system = %d\n", c->delivery_system);
+	
+	realFreq = c->frequency;
+	lpf_offset_KHz = 0;
+	if(c->symbol_rate < 5000000){
+		lpf_offset_KHz = FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+		realFreq += FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+	}
+	
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
+
+	div4 = 0;
+	RFgain = 0;
+	if(state->tuner_id == TS2022_ID){
+		m88ds3103_tuner_writereg(state, 0x10, 0x0a);
+		m88ds3103_tuner_writereg(state, 0x11, 0x40);
+		if (realFreq < 1103000) {
+			m88ds3103_tuner_writereg(state, 0x10, 0x1b);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ;				
+		}else {
+			ndiv = (realFreq * (6 + 8) * 2)/MT_FE_CRYSTAL_KHZ;
+		}
+		ndiv = ndiv + ndiv%2;
+		if(ndiv < 4095)
+			ndiv = ndiv - 1024;
+		else if (ndiv < 6143)
+			ndiv = ndiv + 1024;
+		else
+			ndiv = ndiv + 3072;	
+		
+		m88ds3103_tuner_writereg(state, 0x01, (ndiv & 0x3f00) >> 8);											
+	}else{
+		m88ds3103_tuner_writereg(state, 0x10, 0x00);			
+		if (realFreq < 1146000){
+			m88ds3103_tuner_writereg(state, 0x10, 0x11);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4) / MT_FE_CRYSTAL_KHZ;
+		}else{
+			m88ds3103_tuner_writereg(state, 0x10, 0x01);
+			ndiv = (realFreq * (6 + 8) * 2) / MT_FE_CRYSTAL_KHZ;
+		}
+		ndiv = ndiv + ndiv%2;
+		ndiv = ndiv - 1024;
+		m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8)&0x0f);
+	}
+	/* set pll */
+	m88ds3103_tuner_writereg(state, 0x02, ndiv & 0x00ff);
+	m88ds3103_tuner_writereg(state, 0x03, 0x06);
+	m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	m88ds3103_tuner_writereg(state, 0x50, 0x10);
+	m88ds3103_tuner_writereg(state, 0x50, 0x00);	
+
+	if(state->tuner_id == TS2022_ID){
+		if(( realFreq >= 1650000 ) && (realFreq <= 1850000)){
+			msleep(5);
+			value = m88ds3103_tuner_readreg(state, 0x14);
+			value &= 0x7f;
+			if(value < 64){
+				m88ds3103_tuner_writereg(state, 0x10, 0x82);
+				m88ds3103_tuner_writereg(state, 0x11, 0x6f);
+
+				m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+				m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+				m88ds3103_tuner_writereg(state, 0x50, 0x10);
+				m88ds3103_tuner_writereg(state, 0x50, 0x00);
+			}
+		}
+		msleep(5);
+		value = m88ds3103_tuner_readreg(state, 0x14);
+		value &= 0x1f;
+
+		if(value > 19){
+			value = m88ds3103_tuner_readreg(state, 0x10);
+			value &= 0x1d;
+			m88ds3103_tuner_writereg(state, 0x10, value);
+		}				
+	}else{
+		msleep(5);
+		value = m88ds3103_tuner_readreg(state, 0x66);
+		changePLL = (((value & 0x80) >> 7) != div4);
+
+		if(changePLL){
+			m88ds3103_tuner_writereg(state, 0x10, 0x11);
+			div4 = 1;
+			ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ;
+			ndiv = ndiv + ndiv%2;
+			ndiv = ndiv - 1024;
+					
+			m88ds3103_tuner_writereg(state, 0x01, (ndiv>>8) & 0x0f);
+			m88ds3103_tuner_writereg(state, 0x02, ndiv & 0xff);
+			
+			m88ds3103_tuner_writereg(state, 0x51, 0x0f);
+			m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+			m88ds3103_tuner_writereg(state, 0x50, 0x10);
+			m88ds3103_tuner_writereg(state, 0x50, 0x00);
+		}		
+	}
+	/*set the RF gain*/
+	if(state->tuner_id == TS2020_ID)
+		m88ds3103_tuner_writereg(state, 0x60, 0x79);
+			
+	m88ds3103_tuner_writereg(state, 0x51, 0x17);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	m88ds3103_tuner_writereg(state, 0x50, 0x08);
+	m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+
+	if(state->tuner_id == TS2020_ID){
+		RFgain = m88ds3103_tuner_readreg(state, 0x3d);
+		RFgain &= 0x0f;
+		if(RFgain < 15){
+			if(RFgain < 4) 
+				RFgain = 0;
+			else
+				RFgain = RFgain -3;
+			value = ((RFgain << 3) | 0x01) & 0x79;
+			m88ds3103_tuner_writereg(state, 0x60, value);
+			m88ds3103_tuner_writereg(state, 0x51, 0x17);
+			m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+			m88ds3103_tuner_writereg(state, 0x50, 0x08);
+			m88ds3103_tuner_writereg(state, 0x50, 0x00);
+		}
+	}
+	
+	/* set the LPF */
+	if(state->tuner_id == TS2022_ID){
+		m88ds3103_tuner_writereg(state, 0x25, 0x00);
+		m88ds3103_tuner_writereg(state, 0x27, 0x70);
+		m88ds3103_tuner_writereg(state, 0x41, 0x09);
+		m88ds3103_tuner_writereg(state, 0x08, 0x0b);
+	}
+
+	f3db = ((c->symbol_rate / 1000) *135) / 200 + 2000;
+	f3db += lpf_offset_KHz;
+	if (f3db < 7000)
+		f3db = 7000;
+	if (f3db > 40000)
+		f3db = 40000;
+			
+	gdiv28 = (MT_FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+	m88ds3103_tuner_writereg(state, 0x04, gdiv28 & 0xff);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	m88ds3103_tuner_writereg(state, 0x50, 0x04);
+	m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+
+	value = m88ds3103_tuner_readreg(state, 0x26);
+	capCode = value & 0x3f;
+	if(state->tuner_id == TS2022_ID){
+		m88ds3103_tuner_writereg(state, 0x41, 0x0d);
+
+		m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+		m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+		m88ds3103_tuner_writereg(state, 0x50, 0x04);
+		m88ds3103_tuner_writereg(state, 0x50, 0x00);
+
+		msleep(2);
+
+		value = m88ds3103_tuner_readreg(state, 0x26);
+		value &= 0x3f;
+		value = (capCode + value) / 2;		
+	}
+	else
+		value = capCode;
+		
+	gdiv28 = gdiv28 * 207 / (value * 2 + 151);	
+	mlpf_max = gdiv28 * 135 / 100;
+	mlpf_min = gdiv28 * 78 / 100;
+	if (mlpf_max > 63)
+		mlpf_max = 63;
+
+	if(state->tuner_id == TS2022_ID)
+		lpf_coeff = 3200;
+	else
+		lpf_coeff = 2766;
+		
+	nlpf = (f3db * gdiv28 * 2 / lpf_coeff / (MT_FE_CRYSTAL_KHZ / 1000)  + 1) / 2 ;	
+	if (nlpf > 23) nlpf = 23;
+	if (nlpf < 1) nlpf = 1;
+
+	lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2 / f3db + 1) / 2;
+
+	if (lpf_mxdiv < mlpf_min){
+		nlpf++;
+		lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000) * lpf_coeff * 2  / f3db + 1) / 2;
+	}
+
+	if (lpf_mxdiv > mlpf_max)
+		lpf_mxdiv = mlpf_max;
+
+	m88ds3103_tuner_writereg(state, 0x04, lpf_mxdiv);
+	m88ds3103_tuner_writereg(state, 0x06, nlpf);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	m88ds3103_tuner_writereg(state, 0x50, 0x04);
+	m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	msleep(5);
+	
+	if(state->tuner_id == TS2022_ID){
+		msleep(2);
+		value = m88ds3103_tuner_readreg(state, 0x26);
+		capCode = value & 0x3f;
+
+		m88ds3103_tuner_writereg(state, 0x41, 0x09);
+
+		m88ds3103_tuner_writereg(state, 0x51, 0x1b);
+		m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+		m88ds3103_tuner_writereg(state, 0x50, 0x04);
+		m88ds3103_tuner_writereg(state, 0x50, 0x00);
+
+		msleep(2);
+		value = m88ds3103_tuner_readreg(state, 0x26);
+		value &= 0x3f;
+		value = (capCode + value) / 2;
+
+		value = value | 0x80;
+		m88ds3103_tuner_writereg(state, 0x25, value);
+		m88ds3103_tuner_writereg(state, 0x27, 0x30);
+
+		m88ds3103_tuner_writereg(state, 0x08, 0x09);		
+	}
+
+	/* Set the BB gain */
+	m88ds3103_tuner_writereg(state, 0x51, 0x1e);
+	m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+	m88ds3103_tuner_writereg(state, 0x50, 0x01);
+	m88ds3103_tuner_writereg(state, 0x50, 0x00);
+	if(state->tuner_id == TS2020_ID){
+		if(RFgain == 15){
+			msleep(40);
+			value = m88ds3103_tuner_readreg(state, 0x21);
+			value &= 0x0f;
+			if(value < 3){
+				m88ds3103_tuner_writereg(state, 0x60, 0x61);
+				m88ds3103_tuner_writereg(state, 0x51, 0x17);
+				m88ds3103_tuner_writereg(state, 0x51, 0x1f);
+				m88ds3103_tuner_writereg(state, 0x50, 0x08);
+				m88ds3103_tuner_writereg(state, 0x50, 0x00);
+			}			
+		}
+	}
+	msleep(60);
+	
+	offset_khz = (ndiv - ndiv % 2 + 1024) * MT_FE_CRYSTAL_KHZ
+		/ (6 + 8) / (div4 + 1) / 2 - realFreq;
+
+	m88ds3103_demod_connect(fe, offset_khz+lpf_offset_KHz);
+
+	for (i = 0; i < 30 ; i++) {
+		m88ds3103_read_status(fe, &status);
+		if (status & FE_HAS_LOCK){
+			break;
+                }
+		msleep(20);
+	}
+	
+	if((status & FE_HAS_LOCK) == 0){
+		state->delivery_system = (state->delivery_system == SYS_DVBS) ? SYS_DVBS2 : SYS_DVBS;
+		m88ds3103_demod_connect(fe, offset_khz);
+	
+		for (i = 0; i < 30 ; i++) {
+			m88ds3103_read_status(fe, &status);
+			if (status & FE_HAS_LOCK){
+				break;
+                	}
+			msleep(20);
+		}
+	}
+	
+	if (status & FE_HAS_LOCK){
+		if(state->config->ci_mode == 2)
+			m88ds3103_set_clock_ratio(state);
+		if(state->config->start_ctrl){
+			if(state->first_lock == 0){
+				state->config->start_ctrl(fe);
+				state->first_lock = 1;	
+			}
+		}		
+	}
+		
+	return 0;
+}
+
+static int m88ds3103_tune(struct dvb_frontend *fe,
+			bool re_tune,
+			unsigned int mode_flags,
+			unsigned int *delay,
+			fe_status_t *status)
+{	
+	*delay = HZ / 5;
+	
+	dprintk("%s() ", __func__);
+	dprintk("re_tune = %d\n", re_tune);
+	
+	if (re_tune) {
+		int ret = m88ds3103_set_frontend(fe);
+		if (ret)
+			return ret;
+	}
+	
+	return m88ds3103_read_status(fe, status);
+}
+
+static enum dvbfe_algo m88ds3103_get_algo(struct dvb_frontend *fe)
+{
+	return DVBFE_ALGO_HW;
+}
+ 
+ /*
+ * Power config will reset and load initial firmware if required
+ */
+static int m88ds3103_initilaze(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	int ret;
+
+	dprintk("%s()\n", __func__);
+	/* hard reset */
+	m88ds3103_writereg(state, 0x07, 0x80);
+	m88ds3103_writereg(state, 0x07, 0x00);
+	msleep(1);
+	
+	m88ds3103_writereg(state, 0x08, 0x01 | m88ds3103_readreg(state, 0x08));
+	msleep(1);
+
+	if(state->tuner_id == TS2020_ID){
+		/* TS2020 init */
+		m88ds3103_tuner_writereg(state, 0x42, 0x73);
+		msleep(2);
+		m88ds3103_tuner_writereg(state, 0x05, 0x01);
+		m88ds3103_tuner_writereg(state, 0x62, 0xb5);
+		m88ds3103_tuner_writereg(state, 0x07, 0x02);
+		m88ds3103_tuner_writereg(state, 0x08, 0x01);
+	}
+	else if(state->tuner_id == TS2022_ID){
+		/* TS2022 init */
+		m88ds3103_tuner_writereg(state, 0x62, 0x6c);
+		msleep(2);
+		m88ds3103_tuner_writereg(state, 0x42, 0x6c);
+		msleep(2);
+		m88ds3103_tuner_writereg(state, 0x7d, 0x9d);
+		m88ds3103_tuner_writereg(state, 0x7c, 0x9a);
+		m88ds3103_tuner_writereg(state, 0x7a, 0x76);
+
+		m88ds3103_tuner_writereg(state, 0x3b, 0x01);
+		m88ds3103_tuner_writereg(state, 0x63, 0x88);
+
+		m88ds3103_tuner_writereg(state, 0x61, 0x85);
+		m88ds3103_tuner_writereg(state, 0x22, 0x30);
+		m88ds3103_tuner_writereg(state, 0x30, 0x40);
+		m88ds3103_tuner_writereg(state, 0x20, 0x23);
+		m88ds3103_tuner_writereg(state, 0x24, 0x02);
+		m88ds3103_tuner_writereg(state, 0x12, 0xa0);	
+	}
+		
+	if(state->demod_id == DS3103_ID){
+		m88ds3103_writereg(state, 0x07, 0xe0);
+		m88ds3103_writereg(state, 0x07, 0x00);
+		msleep(1);		
+	}
+	m88ds3103_writereg(state, 0xb2, 0x01);
+	
+	/* Load the firmware if required */
+	ret = m88ds3103_load_firmware(fe);
+	if (ret != 0){
+		printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
+		return ret;
+	}
+	if(state->demod_id == DS3103_ID){
+		m88ds3103_writereg(state, 0x4d, 0xfd & m88ds3103_readreg(state, 0x4d));
+		m88ds3103_writereg(state, 0x30, 0xef & m88ds3103_readreg(state, 0x30));		
+	}
+
+	return 0;
+}
+
+/*
+ * Initialise or wake up device
+ */
+static int m88ds3103_initfe(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+	u8 val;
+
+	dprintk("%s()\n", __func__);
+
+	/* 1st step to wake up demod */
+	m88ds3103_writereg(state, 0x08, 0x01 | m88ds3103_readreg(state, 0x08));
+	m88ds3103_writereg(state, 0x04, 0xfe & m88ds3103_readreg(state, 0x04));
+	m88ds3103_writereg(state, 0x23, 0xef & m88ds3103_readreg(state, 0x23));
+	
+	/* 2nd step to wake up tuner */
+	val = m88ds3103_tuner_readreg(state, 0x00) & 0xff;
+	if((val & 0x01) == 0){
+		m88ds3103_tuner_writereg(state, 0x00, 0x01);
+		msleep(50);
+	}
+	m88ds3103_tuner_writereg(state, 0x00, 0x03);
+	msleep(50);
+	
+	return 0;	
+}
+
+/* Put device to sleep */
+static int m88ds3103_sleep(struct dvb_frontend *fe)
+{
+	struct m88ds3103_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __func__);
+	
+	/* 1st step to sleep tuner */
+	m88ds3103_tuner_writereg(state, 0x00, 0x00);
+	
+	/* 2nd step to sleep demod */
+	m88ds3103_writereg(state, 0x08, 0xfe & m88ds3103_readreg(state, 0x08));
+	m88ds3103_writereg(state, 0x04, 0x01 | m88ds3103_readreg(state, 0x04));
+	m88ds3103_writereg(state, 0x23, 0x10 | m88ds3103_readreg(state, 0x23));
+	
+
+	return 0;
+}
+
+static struct dvb_frontend_ops m88ds3103_ops = {
+	.delsys = { SYS_DVBS, SYS_DVBS2},
+	.info = {
+		.name = "Montage DS3103/TS2022",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
+		.frequency_tolerance = 5000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_2G_MODULATION |
+			FE_CAN_QPSK | FE_CAN_RECOVER
+	},
+
+	.release = m88ds3103_release,
+
+	.init = m88ds3103_initfe,
+	.sleep = m88ds3103_sleep,
+	.read_status = m88ds3103_read_status,
+	.read_ber = m88ds3103_read_ber,
+	.read_signal_strength = m88ds3103_read_signal_strength,
+	.read_snr = m88ds3103_read_snr,
+	.read_ucblocks = m88ds3103_read_ucblocks,
+	.set_tone = m88ds3103_set_tone,
+	.set_voltage = m88ds3103_set_voltage,
+	.diseqc_send_master_cmd = m88ds3103_send_diseqc_msg,
+	.diseqc_send_burst = m88ds3103_diseqc_send_burst,
+	.get_frontend_algo = m88ds3103_get_algo,
+	.tune = m88ds3103_tune,
+	.set_frontend = m88ds3103_set_frontend,
+};
+
+MODULE_DESCRIPTION("DVB Frontend module for Montage DS3103/TS2022 hardware");
+MODULE_AUTHOR("Max nibble");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/m88ds3103.h b/drivers/media/dvb/frontends/m88ds3103.h
new file mode 100644
index 0000000..c7b690e
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103.h
@@ -0,0 +1,53 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef M88DS3103_H
+#define M88DS3103_H
+
+#include <linux/dvb/frontend.h>
+
+struct m88ds3103_config {
+	/* the demodulator's i2c address */
+	u8 demod_address;
+	u8 ci_mode;
+	u8 pin_ctrl;
+	u8 ts_mode; /* 0: Parallel, 1: Serial */
+
+	/* Set device param to start dma */
+	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+    /* Start to transfer data */
+    int (*start_ctrl)(struct dvb_frontend *fe);
+    /* Set LNB voltage */
+    int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+};
+
+#if defined(CONFIG_DVB_M88DS3103) || \
+	(defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
+extern struct dvb_frontend *m88ds3103_attach(
+       const struct m88ds3103_config *config,
+       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *m88ds3103_attach(
+       const struct m88ds3103_config *config,
+       struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_M88DS3103 */
+#endif /* M88DS3103_H */
diff --git a/drivers/media/dvb/frontends/m88ds3103_priv.h b/drivers/media/dvb/frontends/m88ds3103_priv.h
new file mode 100644
index 0000000..2838514
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103_priv.h
@@ -0,0 +1,403 @@
+/*
+    Montage Technology M88DS3103/M88TS2022 - DVBS/S2 Satellite demod/tuner driver
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef M88DS3103_PRIV_H
+#define M88DS3103_PRIV_H
+
+#define FW_DOWN_SIZE 32
+#define FW_DOWN_LOOP (8192/FW_DOWN_SIZE)
+#define DS3103_DEFAULT_FIRMWARE "dvb-fe-ds3103.fw"
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds300x.fw"
+#define MT_FE_MCLK_KHZ 96000 /* in kHz */
+#define MT_FE_CRYSTAL_KHZ   27000 /* in kHz */
+#define FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz 3000
+#define DS3000_ID	0x3000
+#define DS3103_ID	0x3103
+#define TS2020_ID	0x2020
+#define TS2022_ID	0x2022
+#define UNKNOW_ID	0x0000
+
+struct m88ds3103_state {
+	struct i2c_adapter *i2c;
+	const struct m88ds3103_config *config;
+	
+	struct dvb_frontend frontend;
+	
+	u32 preBer;
+	u8 skip_fw_load;	
+	u8 first_lock; /* The first time of signal lock */
+	u16 demod_id; /* demod chip type */
+	u16 tuner_id; /* tuner chip type */
+	fe_delivery_system_t delivery_system;
+};
+
+/* For M88DS3103 demod dvbs mode.*/
+static u8 ds3103_dvbs_init_tab[] = {
+	0x23, 0x07,
+	0x08, 0x03,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x40,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x0f,
+	0x64, 0x30,
+	0x65, 0x40,
+	0x68, 0x26,
+	0x69, 0x4c,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x38,
+	0x77, 0xa6,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x14,
+	0x7c, 0x00,
+	0xae, 0x82,
+	0x80, 0x64,
+	0x81, 0x66,
+	0x82, 0x44,
+	0x85, 0x04,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xff,
+	0xc7, 0x00,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xe0, 0xf8,
+	0xe6, 0x8b,
+	0xd0, 0x40,
+	0xf8, 0x20,
+	0xfa, 0x0f,
+	0x00, 0x00,
+	0xbd, 0x01,
+	0xb8, 0x00,
+};
+/* For M88DS3103 demod dvbs2 mode.*/
+static u8 ds3103_dvbs2_init_tab[] = {
+	0x23, 0x07,
+	0x08, 0x07,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x0f,
+	0x64, 0x10,
+	0x65, 0x20,
+	0x68, 0x46,
+	0x69, 0xcd,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x38,
+	0x77, 0xa6,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x14,
+	0x85, 0x08,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0x86, 0x00,
+	0x87, 0x0f,
+	0x89, 0x00,
+	0x8b, 0x44,
+	0x8c, 0x66,
+	0x9d, 0xc1,
+	0x8a, 0x10,
+	0xad, 0x40,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc1, 0x10,
+	0xc2, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xff,
+	0xc7, 0x00,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xca, 0x23,
+	0xcb, 0x24,
+	0xcc, 0xf4,
+	0xce, 0x74,
+	0x00, 0x00,
+	0xbd, 0x01,
+	0xb8, 0x00,
+};
+
+/* For M88DS3000 demod dvbs mode.*/
+static u8 ds3000_dvbs_init_tab[] = {
+	0x23, 0x05,
+	0x08, 0x03,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x40,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x40,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0xc8,
+	0x50, 0x77,
+	0x51, 0x77,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x56, 0x01,
+	0x63, 0x47,
+	0x64, 0x30,
+	0x65, 0x40,
+	0x68, 0x26,
+	0x69, 0x4c,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0x76, 0x00,
+	0x77, 0xd1,
+	0x78, 0x0c,
+	0x79, 0x80,
+	0x7f, 0x04,
+	0x7c, 0x00,
+	0x80, 0x86,
+	0x81, 0xa6,
+	0x85, 0x04,
+	0xcd, 0xf4,
+	0x90, 0x33,
+	0xa0, 0x44,
+	0xc0, 0x18,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0x80,
+	0xc6, 0x80,
+	0xc7, 0x0a,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xfe, 0xb6,
+	0xe0, 0xf8,
+	0xe6, 0x8b,
+	0xd0, 0x40,
+	0xf8, 0x20,
+	0xfa, 0x0f,
+	0xad, 0x20,
+	0xae, 0x07,
+	0xb8, 0x00,
+};
+
+/* For M88DS3000 demod dvbs2 mode.*/
+static u8 ds3000_dvbs2_init_tab[] = {
+	0x23, 0x0f,
+	0x08, 0x07,
+	0x0c, 0x02,
+	0x21, 0x54,
+	0x25, 0x82,
+	0x27, 0x31,
+	0x30, 0x08,
+	0x31, 0x32,
+	0x32, 0x32,
+	0x33, 0x35,
+	0x35, 0xff,
+	0x3a, 0x00,
+	0x37, 0x10,
+	0x38, 0x10,
+	0x39, 0x02,
+	0x42, 0x60,
+	0x4a, 0x80,
+	0x4b, 0x04,
+	0x4d, 0x91,
+	0x5d, 0x88,
+	0x50, 0x36,
+	0x51, 0x36,
+	0x52, 0x36,
+	0x53, 0x36,
+	0x63, 0x60,
+	0x64, 0x10,
+	0x65, 0x10,
+	0x68, 0x04,
+	0x69, 0x29,
+	0x70, 0x20,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x40,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x60,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x80,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0xa0,
+	0x71, 0x70,
+	0x72, 0x04,
+	0x73, 0x00,
+	0x70, 0x1f,
+	0xa0, 0x44,
+	0xc0, 0x08,
+	0xc1, 0x10,
+	0xc2, 0x08,
+	0xc3, 0x10,
+	0xc4, 0x08,
+	0xc5, 0xf0,
+	0xc6, 0xf0,
+	0xc7, 0x0a,
+	0xc8, 0x1a,
+	0xc9, 0x80,
+	0xca, 0x23,
+	0xcb, 0x24,
+	0xce, 0x74,
+	0x56, 0x01,
+	0x90, 0x03,
+	0x76, 0x80,
+	0x77, 0x42,
+	0x78, 0x0a,
+	0x79, 0x80,
+	0xad, 0x40,
+	0xae, 0x07,
+	0x7f, 0xd4,
+	0x7c, 0x00,
+	0x80, 0xa8,
+	0x81, 0xda,
+	0x7c, 0x01,
+	0x80, 0xda,
+	0x81, 0xec,
+	0x7c, 0x02,
+	0x80, 0xca,
+	0x81, 0xeb,
+	0x7c, 0x03,
+	0x80, 0xba,
+	0x81, 0xdb,
+	0x85, 0x08,
+	0x86, 0x00,
+	0x87, 0x02,
+	0x89, 0x80,
+	0x8b, 0x44,
+	0x8c, 0xaa,
+	0x8a, 0x10,
+	0xba, 0x00,
+	0xf5, 0x04,
+	0xd2, 0x32,
+	0xb8, 0x00,
+};
+
+#endif /* M88DS3103_PRIV_H */
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 49ce266..e6a882b 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
 			rc-dm1105-nec.o \
 			rc-dntv-live-dvb-t.o \
 			rc-dntv-live-dvbt-pro.o \
+			rc-dvbsky.o \
 			rc-em-terratec.o \
 			rc-encore-enltv2.o \
 			rc-encore-enltv.o \
diff --git a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c
new file mode 100644
index 0000000..2bd9977
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dvbsky.c
@@ -0,0 +1,78 @@
+/* rc-dvbsky.c - Keytable for Dvbsky Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ *
+ * Copyright (c) 2010-2011 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+/*
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_dvbsky[] = {
+	{ 0x0000, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },	
+	{ 0x000a, KEY_MUTE },
+	{ 0x000d, KEY_OK },
+	{ 0x000b, KEY_STOP },
+	{ 0x000c, KEY_EXIT },	
+	{ 0x000e, KEY_CAMERA }, /*Snap shot*/
+	{ 0x000f, KEY_SUBTITLE }, /*PIP*/
+	{ 0x0010, KEY_VOLUMEUP },
+	{ 0x0011, KEY_VOLUMEDOWN },
+	{ 0x0012, KEY_FAVORITES },
+	{ 0x0013, KEY_LIST }, /*Info*/
+	{ 0x0016, KEY_PAUSE },
+	{ 0x0017, KEY_PLAY },
+	{ 0x001f, KEY_RECORD },
+	{ 0x0020, KEY_CHANNELDOWN },
+	{ 0x0021, KEY_CHANNELUP },
+	{ 0x0025, KEY_POWER2 },
+	{ 0x0026, KEY_REWIND },
+	{ 0x0027, KEY_FASTFORWARD },
+	{ 0x0029, KEY_LAST },
+	{ 0x002b, KEY_MENU },	
+	{ 0x002c, KEY_EPG },
+	{ 0x002d, KEY_ZOOM },	
+};
+
+static struct rc_map_list rc5_dvbsky_map = {
+	.map = {
+		.scan    = rc5_dvbsky,
+		.size    = ARRAY_SIZE(rc5_dvbsky),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_DVBSKY,
+	}
+};
+
+static int __init init_rc_map_rc5_dvbsky(void)
+{
+	return rc_map_register(&rc5_dvbsky_map);
+}
+
+static void __exit exit_rc_map_rc5_dvbsky(void)
+{
+	rc_map_unregister(&rc5_dvbsky_map);
+}
+
+module_init(init_rc_map_rc5_dvbsky)
+module_exit(exit_rc_map_rc5_dvbsky)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index b391e9b..20337c7 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -20,6 +20,7 @@ config VIDEO_CX23885
 	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
 	select DVB_STV6110 if !DVB_FE_CUSTOMISE
 	select DVB_CX24116 if !DVB_FE_CUSTOMISE
+	select DVB_M88DS3103 if !DVB_FE_CUSTOMISE
 	select DVB_STV0900 if !DVB_FE_CUSTOMISE
 	select DVB_DS3000 if !DVB_FE_CUSTOMISE
 	select DVB_STV0367 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 19b5499..c707f19 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -497,7 +497,31 @@ struct cx23885_board cx23885_boards[] = {
 		.name		= "TerraTec Cinergy T PCIe Dual",
 		.portb		= CX23885_MPEG_DVB,
 		.portc		= CX23885_MPEG_DVB,
-	}
+	},
+
+	[CX23885_BOARD_BST_PS8512] = {
+		.name		= "Bestunar PS8512",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S950] = {
+		.name		= "DVBSKY S950",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S952] = {
+		.name		= "DVBSKY S952",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_S950_CI] = {
+		.ci_type	= 3,
+		.name		= "DVBSKY S950CI DVB-S2 CI",
+		.portb		= CX23885_MPEG_DVB,
+	},
+	[CX23885_BOARD_DVBSKY_C2800E_CI] = {
+		.ci_type	= 3,
+		.name		= "DVBSKY C2800E DVB-C CI",
+		.portb		= CX23885_MPEG_DVB,
+	},	
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -705,6 +729,26 @@ struct cx23885_subid cx23885_subids[] = {
 		.subvendor = 0x153b,
 		.subdevice = 0x117e,
 		.card      = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8512,
+		.card      = CX23885_BOARD_BST_PS8512,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x0950,
+		.card      = CX23885_BOARD_DVBSKY_S950,		
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x0952,
+		.card      = CX23885_BOARD_DVBSKY_S952,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x950C,
+		.card      = CX23885_BOARD_DVBSKY_S950_CI,
+	}, {
+		.subvendor = 0x4254,
+		.subdevice = 0x2800,
+		.card      = CX23885_BOARD_DVBSKY_C2800E_CI,
 	},
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1216,9 +1260,83 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 		/* enable irq */
 		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
 		break;
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_BST_PS8512:			
+		cx23885_gpio_enable(dev, GPIO_2, 1);
+		cx23885_gpio_clear(dev, GPIO_2);
+		msleep(100);		
+		cx23885_gpio_set(dev, GPIO_2);
+		break;
+	case CX23885_BOARD_DVBSKY_S952:		
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		
+		cx23885_gpio_enable(dev, GPIO_2, 1);
+		cx23885_gpio_enable(dev, GPIO_11, 1);
+		
+		cx23885_gpio_clear(dev, GPIO_2);
+		cx23885_gpio_clear(dev, GPIO_11);
+		msleep(100);		
+		cx23885_gpio_set(dev, GPIO_2);
+		cx23885_gpio_set(dev, GPIO_11);	
+		break;
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
+		/* GPIO-0 INTA from CiMax, input
+		   GPIO-1 reset CiMax, output, high active
+		   GPIO-2 reset demod, output, low active
+		   GPIO-3 to GPIO-10 data/addr for CAM
+		   GPIO-11 ~CS0 to CiMax1
+		   GPIO-12 ~CS1 to CiMax2
+		   GPIO-13 ADL0 load LSB addr
+		   GPIO-14 ADL1 load MSB addr
+		   GPIO-15 ~RDY from CiMax
+		   GPIO-17 ~RD to CiMax
+		   GPIO-18 ~WR to CiMax
+		 */
+		cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */
+		cx_clear(GP0_IO, 0x00010004); /*GPIO 0 as input*/
+		mdelay(100);/* reset delay */
+		cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */
+		cx_clear(GP0_IO, 0x00010002);
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		/* GPIO-15 IN as ~ACK, rest as OUT */
+		cx_write(MC417_OEN, 0x00001000);
+		/* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+		cx_write(MC417_RWD, 0x0000c300);
+		/* enable irq */
+		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+		break;
 	}
 }
 
+static int cx23885_ir_patch(struct i2c_adapter *i2c, u8 reg, u8 mask)
+{
+	struct i2c_msg msgs[2];
+	u8 tx_buf[2], rx_buf[1];
+	/* Write register address */
+	tx_buf[0] = reg;
+	msgs[0].addr = 0x4c;
+	msgs[0].flags = 0;
+	msgs[0].len = 1;
+	msgs[0].buf = (char *) tx_buf;
+	/* Read data from register */
+	msgs[1].addr = 0x4c;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = 1;
+	msgs[1].buf = (char *) rx_buf;	
+	
+	i2c_transfer(i2c, msgs, 2);
+
+	tx_buf[0] = reg;
+	tx_buf[1] = rx_buf[0] | mask;
+	msgs[0].addr = 0x4c;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (char *) tx_buf;
+	
+	return i2c_transfer(i2c, msgs, 1);
+}
+
 int cx23885_ir_init(struct cx23885_dev *dev)
 {
 	static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
@@ -1301,6 +1419,22 @@ int cx23885_ir_init(struct cx23885_dev *dev)
 		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
 				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:	
+		dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+		if (dev->sd_ir == NULL) {
+			ret = -ENODEV;
+			break;
+		}
+		v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+				 ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+				 
+		cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x1f,0x80);
+		cx23885_ir_patch(&(dev->i2c_bus[2].i2c_adap),0x23,0x80);
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 		if (!enable_885_ir)
 			break;
@@ -1332,6 +1466,11 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
 		break;
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:	
 		cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
 		/* sd_ir is a duplicate pointer to the AV Core, just clear it */
 		dev->sd_ir = NULL;
@@ -1375,6 +1514,11 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
 		break;
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:	
 		if (dev->sd_ir)
 			cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
 		break;
@@ -1459,6 +1603,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_DVBWORLD_2005:
 		ts1->gen_ctrl_val  = 0x5; /* Parallel */
@@ -1489,6 +1637,14 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_DVBSKY_S952:
+		ts1->gen_ctrl_val  = 0x5; /* Parallel */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xe; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -1541,6 +1697,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 	case CX23885_BOARD_MPX885:
 	case CX23885_BOARD_MYGICA_X8507:
 	case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+	case CX23885_BOARD_DVBSKY_C2800E_CI:
 		dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
 				&dev->i2c_bus[2].i2c_adap,
 				"cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 6835eb1..e5b39b5 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -51,6 +51,7 @@
 #include "stv6110.h"
 #include "lnbh24.h"
 #include "cx24116.h"
+#include "m88ds3103.h"
 #include "cimax2.h"
 #include "lgs8gxx.h"
 #include "netup-eeprom.h"
@@ -489,6 +490,37 @@ static struct xc5000_config mygica_x8506_xc5000_config = {
 	.if_khz = 5380,
 };
 
+/* bestunar single dvb-s2 */
+static struct m88ds3103_config bst_ds3103_config = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+	.set_voltage = bst_set_voltage,
+};
+/* DVBSKY dual dvb-s2 */
+static struct m88ds3103_config dvbsky_ds3103_config_pri = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+	.set_voltage = bst_set_voltage,	
+};
+static struct m88ds3103_config dvbsky_ds3103_config_sec = {
+	.demod_address = 0x68,
+	.ci_mode = 0,
+	.pin_ctrl = 0x82,
+	.ts_mode = 1,
+	.set_voltage = dvbsky_set_voltage_sec,	
+};
+
+static struct m88ds3103_config dvbsky_ds3103_ci_config = {
+	.demod_address = 0x68,
+	.ci_mode = 2,
+	.pin_ctrl = 0x82,
+	.ts_mode = 0,
+};
+
 static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
 {
 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
@@ -1173,6 +1205,41 @@ static int dvb_register(struct cx23885_tsport *port)
 			break;
 		}
 		break;
+
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+		i2c_bus = &dev->i2c_bus[1];	
+		fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+					&bst_ds3103_config,
+					&i2c_bus->i2c_adap);
+		break;	
+			
+	case CX23885_BOARD_DVBSKY_S952:
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			i2c_bus = &dev->i2c_bus[1];
+			fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+						&dvbsky_ds3103_config_pri,
+						&i2c_bus->i2c_adap);
+			break;
+		/* port C */
+		case 2:
+			i2c_bus = &dev->i2c_bus[0];
+			fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+						&dvbsky_ds3103_config_sec,
+						&i2c_bus->i2c_adap);	
+			break;
+		}
+		break;
+
+	case CX23885_BOARD_DVBSKY_S950_CI:
+		i2c_bus = &dev->i2c_bus[1];	
+		fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+					&dvbsky_ds3103_ci_config,
+					&i2c_bus->i2c_adap);
+		break;
+				
 	default:
 		printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
 			" isn't supported yet\n",
@@ -1221,7 +1288,7 @@ static int dvb_register(struct cx23885_tsport *port)
 		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
 			port->nr, port->frontends.adapter.proposed_mac);
 
-		netup_ci_init(port);
+		netup_ci_init(port, false);
 		break;
 		}
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
@@ -1248,6 +1315,36 @@ static int dvb_register(struct cx23885_tsport *port)
 		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
 		break;
 		}
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:{
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if(port->nr > 2)
+			break;
+
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + 
+			(port->nr-1)*8, 6);
+		break;
+		}
+	case CX23885_BOARD_DVBSKY_S950_CI: {
+		u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+		if(port->nr > 2)
+			break;
+
+		dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+		tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+		printk(KERN_INFO "DVBSKY PCIe MAC= %pM\n", eeprom + 0xc0+(port->nr-1)*8);
+		memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + 
+			(port->nr-1)*8, 6);
+			
+		netup_ci_init(port, true);
+		break;
+		}
 	}
 
 	return ret;
@@ -1330,6 +1427,7 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
 
 	switch (port->dev->board) {
 	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+	case CX23885_BOARD_DVBSKY_S950_CI:
 		netup_ci_exit(port);
 		break;
 	case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c
index 93998f2..f8b8c55 100644
--- a/drivers/media/video/cx23885/cx23885-f300.c
+++ b/drivers/media/video/cx23885/cx23885-f300.c
@@ -175,3 +175,58 @@ int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 
 	return f300_xfer(fe, buf);
 }
+
+/* bst control */
+int bst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+	
+	cx23885_gpio_enable(dev, GPIO_1, 1);
+	cx23885_gpio_enable(dev, GPIO_0, 1);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		cx23885_gpio_set(dev, GPIO_1);
+		cx23885_gpio_clear(dev, GPIO_0);
+		break;
+	case SEC_VOLTAGE_18:
+		cx23885_gpio_set(dev, GPIO_1);
+		cx23885_gpio_set(dev, GPIO_0);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx23885_gpio_clear(dev, GPIO_1);
+		cx23885_gpio_clear(dev, GPIO_0);
+		break;
+	}
+	
+
+	return 0;
+}
+
+int dvbsky_set_voltage_sec(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct cx23885_tsport *port = fe->dvb->priv;
+	struct cx23885_dev *dev = port->dev;
+	
+	cx23885_gpio_enable(dev, GPIO_12, 1);
+	cx23885_gpio_enable(dev, GPIO_13, 1);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		cx23885_gpio_set(dev, GPIO_13);
+		cx23885_gpio_clear(dev, GPIO_12);
+		break;
+	case SEC_VOLTAGE_18:
+		cx23885_gpio_set(dev, GPIO_13);
+		cx23885_gpio_set(dev, GPIO_12);
+		break;
+	case SEC_VOLTAGE_OFF:
+		cx23885_gpio_clear(dev, GPIO_13);
+		cx23885_gpio_clear(dev, GPIO_12);
+		break;
+	}
+	
+
+	return 0;
+}
\ No newline at end of file
diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h
index e73344c..f93f37d 100644
--- a/drivers/media/video/cx23885/cx23885-f300.h
+++ b/drivers/media/video/cx23885/cx23885-f300.h
@@ -1,2 +1,8 @@
+extern int dvbsky_set_voltage_sec(struct dvb_frontend *fe,
+				fe_sec_voltage_t voltage);
+				
+extern int bst_set_voltage(struct dvb_frontend *fe,
+				fe_sec_voltage_t voltage);
+
 extern int f300_set_voltage(struct dvb_frontend *fe,
 				fe_sec_voltage_t voltage);
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
index ce765e3..2026ec1 100644
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -87,6 +87,10 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_TEVII_S470:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
 		/*
 		 * The only boards we handle right now.  However other boards
 		 * using the CX2388x integrated IR controller should be similar
@@ -138,6 +142,10 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev)
 	case CX23885_BOARD_HAUPPAUGE_HVR1850:
 	case CX23885_BOARD_HAUPPAUGE_HVR1290:
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
 		/*
 		 * The IR controller on this board only returns pulse widths.
 		 * Any other mode setting will fail to set up the device.
@@ -279,6 +287,16 @@ int cx23885_input_init(struct cx23885_dev *dev)
 		/* A guess at the remote */
 		rc_map = RC_MAP_TEVII_NEC;
 		break;
+	case CX23885_BOARD_BST_PS8512:
+	case CX23885_BOARD_DVBSKY_S950:
+	case CX23885_BOARD_DVBSKY_S952:
+	case CX23885_BOARD_DVBSKY_S950_CI:
+		/* Integrated CX2388[58] IR controller */
+		driver_type = RC_DRIVER_IR_RAW;
+		allowed_protos = RC_TYPE_ALL;
+		/* A guess at the remote */
+		rc_map = RC_MAP_DVBSKY;
+		break;
 	default:
 		return -ENODEV;
 	}
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index f020f05..0e22e44 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -89,6 +89,10 @@
 #define CX23885_BOARD_MPX885                   32
 #define CX23885_BOARD_MYGICA_X8507             33
 #define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
+#define CX23885_BOARD_BST_PS8512				35
+#define CX23885_BOARD_DVBSKY_S952				36
+#define CX23885_BOARD_DVBSKY_S950				37
+#define CX23885_BOARD_DVBSKY_S950_CI			38
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
@@ -227,7 +231,7 @@ struct cx23885_board {
 	 */
 	u32			clk_freq;
 	struct cx23885_input    input[MAX_CX23885_INPUT];
-	int			ci_type; /* for NetUP */
+	int			ci_type; /* 1 and 2 for NetUP, 3 for DVBSky. */
 	/* Force bottom field first during DMA (888 workaround) */
 	u32                     force_bff;
 };
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 3598dc0..0daef63 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -57,6 +57,7 @@ config VIDEO_CX88_DVB
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
 	select DVB_S5H1411 if !DVB_FE_CUSTOMISE
 	select DVB_CX24116 if !DVB_FE_CUSTOMISE
+	select DVB_M88DS3103 if !DVB_FE_CUSTOMISE
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_STV0288 if !DVB_FE_CUSTOMISE
 	select DVB_STB6000 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index cbd5d11..d4e576e 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -2309,6 +2309,18 @@ static const struct cx88_board cx88_boards[] = {
 		} },
 		.mpeg           = CX88_MPEG_DVB,
 	},
+	[CX88_BOARD_BST_PS8312] = {
+		.name           = "Bestunar PS8312 DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		} },
+		.mpeg           = CX88_MPEG_DVB,
+	},
 };
 
 /* ------------------------------------------------------------------ */
@@ -2813,6 +2825,10 @@ static const struct cx88_subid cx88_subids[] = {
 		.subvendor = 0x1822,
 		.subdevice = 0x0023,
 		.card      = CX88_BOARD_TWINHAN_VP1027_DVBS,
+	}, {
+		.subvendor = 0x14f1,
+		.subdevice = 0x8312,
+		.card      = CX88_BOARD_BST_PS8312,
 	},
 };
 
@@ -3547,6 +3563,12 @@ static void cx88_card_setup(struct cx88_core *core)
 		cx_write(MO_SRST_IO, 1);
 		msleep(100);
 		break;
+	case  CX88_BOARD_BST_PS8312:
+		cx_write(MO_GP1_IO, 0x808000);
+		msleep(100);
+		cx_write(MO_GP1_IO, 0x808080);
+		msleep(100);		
+		break;	
 	} /*end switch() */
 
 
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 003937c..b1bb055 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -54,6 +54,7 @@
 #include "stv0288.h"
 #include "stb6000.h"
 #include "cx24116.h"
+#include "m88ds3103.h"
 #include "stv0900.h"
 #include "stb6100.h"
 #include "stb6100_proc.h"
@@ -458,6 +459,56 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
 		return core->prev_set_voltage(fe, voltage);
 	return 0;
 }
+/*CX88_BOARD_BST_PS8312*/
+static int bst_dvbs_set_voltage(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_write(MO_GP1_IO, 0x111111);
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			cx_write(MO_GP1_IO, 0x020200);
+			break;
+		case SEC_VOLTAGE_18:
+			cx_write(MO_GP1_IO, 0x020202);
+			break;
+		case SEC_VOLTAGE_OFF:
+			cx_write(MO_GP1_IO, 0x111100);
+			break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
+
+static int bst_dvbs_set_voltage_v2(struct dvb_frontend *fe,
+				      fe_sec_voltage_t voltage)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	cx_write(MO_GP1_IO, 0x111101);
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			cx_write(MO_GP1_IO, 0x020200);
+			break;
+		case SEC_VOLTAGE_18:
+
+			cx_write(MO_GP1_IO, 0x020202);
+			break;
+		case SEC_VOLTAGE_OFF:
+
+			cx_write(MO_GP1_IO, 0x111110);
+			break;
+	}
+
+	if (core->prev_set_voltage)
+		return core->prev_set_voltage(fe, voltage);
+	return 0;
+}
 
 static int vp1027_set_voltage(struct dvb_frontend *fe,
 				    fe_sec_voltage_t voltage)
@@ -700,6 +751,11 @@ static struct ds3000_config tevii_ds3000_config = {
 	.set_ts_params = ds3000_set_ts_param,
 };
 
+static struct m88ds3103_config dvbsky_ds3103_config = {
+	.demod_address = 0x68,
+	.set_ts_params = ds3000_set_ts_param,
+};
+
 static const struct stv0900_config prof_7301_stv0900_config = {
 	.demod_address = 0x6a,
 /*	demod_mode = 0,*/
@@ -1470,6 +1526,35 @@ static int dvb_register(struct cx8802_dev *dev)
 			fe0->dvb.frontend->ops.set_voltage =
 							tevii_dvbs_set_voltage;
 		break;
+	case CX88_BOARD_BST_PS8312:
+		fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+						&dvbsky_ds3103_config,
+						&core->i2c_adap);
+		if (fe0->dvb.frontend != NULL){
+			int ret;
+			u8 b0[] = { 0x60 };
+			u8 b1[2] = { 0 };
+			struct i2c_msg msg[] = {
+				{
+				.addr = 0x50,
+				.flags = 0,
+				.buf = b0,
+				.len = 1
+				}, {
+				.addr = 0x50,
+				.flags = I2C_M_RD,
+				.buf = b1,
+				.len = 2
+				}
+			};
+			ret = i2c_transfer(&core->i2c_adap, msg, 2);
+			printk("PS8312: config = %02x, %02x", b1[0],b1[1]);
+			if(b1[0] == 0xaa)
+				fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage_v2;
+			else			
+				fe0->dvb.frontend->ops.set_voltage = bst_dvbs_set_voltage;
+		}
+		break;
 	case CX88_BOARD_OMICOM_SS4_PCI:
 	case CX88_BOARD_TBS_8920:
 	case CX88_BOARD_PROF_7300:
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index ebf448c..bcdcc8f 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -419,6 +419,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
 		rc_type          = RC_TYPE_NEC;
 		ir->sampling     = 0xff00; /* address */
 		break;
+	case CX88_BOARD_BST_PS8312: 
+		ir_codes         = RC_MAP_DVBSKY;
+		ir->sampling     = 0xff00; /* address */
+		break;	
 	}
 
 	if (!ir_codes) {
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index c9659de..a988f14 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -246,6 +246,7 @@ extern const struct sram_channel const cx88_sram_channels[];
 #define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
 #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
+#define CX88_BOARD_BST_PS8312              91
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 8db6741..7176dac 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -85,6 +85,7 @@ void rc_map_init(void);
 #define RC_MAP_DM1105_NEC                "rc-dm1105-nec"
 #define RC_MAP_DNTV_LIVE_DVBT_PRO        "rc-dntv-live-dvbt-pro"
 #define RC_MAP_DNTV_LIVE_DVB_T           "rc-dntv-live-dvb-t"
+#define RC_MAP_DVBSKY                    "rc-dvbsky"
 #define RC_MAP_EMPTY                     "rc-empty"
 #define RC_MAP_EM_TERRATEC               "rc-em-terratec"
 #define RC_MAP_ENCORE_ENLTV2             "rc-encore-enltv2"
-- 
1.8.2.2

